azure-webjobs-sdk-extensions icon indicating copy to clipboard operation
azure-webjobs-sdk-extensions copied to clipboard

Blob binding with ICloudBlob automatically sets BlobType to BlockBlob

Open ghost opened this issue 5 years ago • 5 comments

A blob binding where ICloudBlob is used, the binding's BlobType is automatically set as BlockBlob. This prevents the use case where the BlobType isn't known at compile time and you just want a catch all for all types. If the blob is actually anything else besides a BlockBlob, it fails to interact with any method in the framework, unless you explicitly use CloudAppendBlob or CloudPagedBlob as type instead of ICloudBlob.

Repro steps

I'm currently using the IBinder since in my case, because I also don't know the actual path yet until later in the code, but I left that part out for clarity.

public async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req,
    IBinder binder)
{
    var path = "some-container/some-path.ext";
    var blob = await binder.BindAsync<ICloudBlob>(new BlobAttribute(path, FileAccess.ReadWrite));
    var contents = await blob.DownloadToByteArrayAsync(); // Fails if BlobType is anything else besides BlockType
}

Expected behavior

In the above example, DownloadToByteArrayAsync() should work, regardless of the actual blob type in the storage. It might be solvable by having the BlobType property set to Unspecified instead of BlockBlob, based on my workaround. However, I do not know the inner workings of the bindings, and sadly I can't test it due to the property only having a getter.

Actual behavior

The current code throws the exception Blob type of the blob reference doesn't match blob type of the blob..

Known workarounds

By using the binding to get the preconfigured connection settings, and then re-getting the blob through the container, you get a CloudBlob that has its BlobType set to Unspecified. This allows the call FetchAttributesAsync() to get the actual blob type in order to re-get a blob reference with correct type. Sadly GetBlobReference() returns a CloudBlob which doesn't implement ICloudBlob so in order to get one that does implement it (CloudAppendBlob, CloudBlockBlob, CloudPagedBlob) additional code is necessary.

var blobFromBinding = await binder.BindAsync<ICloudBlob>(new BlobAttribute(path, FileAccess.ReadWrite));
var blobFromContainer = blobFromBinding.Container.GetBlobReference(blobFromBinding.Name);
await blobFromContainer.FetchAttributesAsync();
var blobWithCorrectType = blobFromContainer.BlobType switch
{
    BlobType.AppendBlob => blobFromBinding.Container.GetAppendBlobReference(blobFromBinding.Name),
    BlobType.PagedBlob => blobFromBinding.Container.GetPagedBlobReference(blobFromBinding.Name),
    _ => blobFromBinding.Container.GetBlockBlobReference(blobFromBinding.Name)
};

Related information

At time of writing, I'm using the latest storage package, which is v4.0.2.

ghost avatar Aug 18 '20 07:08 ghost

Hi @b10-dslappendel ,Apologies for the delayed response, Can we know if you were able to find a solution here. Are you still facing this error

v-anvari avatar Feb 23 '21 12:02 v-anvari

@v-anvari Other than using the workaround I've already given (or by using the v12 storage library directly and forgoing the blob bindings entirely), I haven't found a proper solution using the function bindings. And I can confirm that version 4.0.4 is still having this issue.

ghost avatar Feb 25 '21 15:02 ghost

You might have created blob in different way and reading it by taking its reference in different way. Try write the code in below way for creating and updating the file both

public async Task<CloudAppendBlob> UpdateCSVinBlob(Stream fileStream, string containerName, string blobFullPath)
        {
            // Get the blob container
            var containerClient = this.GetContainer(containerName);
            if (containerClient != null)
            {
                CloudAppendBlob appBlob = containerClient.GetAppendBlobReference(blobFullPath);

                if (!appBlob.Exists())
                {
                    appBlob.CreateOrReplace();
                }

                appBlob.AppendFromStream(fileStream);

                return appBlob;
            }

            return null;
        }

Preeti1910 avatar Jan 28 '22 12:01 Preeti1910

No I haven't. Also, my issue has to do with the Azure functions blob binding.

ghost avatar Jan 28 '22 15:01 ghost

As I'll be removing my work account in favor of my main account, I can contacted at @Archomeda from now on for further updates.

ghost avatar Mar 09 '22 12:03 ghost