Jan Möller
Jan Möller

Reputation: 321

.NET Core: Reading Azure Storage Blob into Memory Stream throws NotSupportedException in HttpBaseStream

I want to download a storage blob from Azure and stream it to a client via an .NET Web-App. The blob was uploaded correctly and is visible in my Azure storage account.

Surprisingly, the following throws an exception within HttpBaseStream:

    [...] 
    var blobClient = _containerClient.GetBlobClient(Path.Combine(fileName));
    var stream = await blobClient.OpenReadAsync();
    return stream;

enter image description here

-> When i step further and return a File (return File(stream, MediaTypeNames.Application.Octet);), the download works as intended.

I tried to push the stream into an MemoryStream, which also fails with the same exception:

    [...] 
    var blobClient = _containerClient.GetBlobClient(Path.Combine(fileName));
    var stream = new MemoryStream();
    await blobClient.DownloadToAsync(stream);
    return stream

->When i step further, returning the file results in a timeout.

How can i fix that? Why do i get this exception - i followed the official quickstart guide from Microsoft.

Upvotes: 0

Views: 4031

Answers (2)

Rahul Shukla
Rahul Shukla

Reputation: 716

Check this sample of all the blob options which i have already posted on git working as expected. Reference

public void DownloadBlob(string path)
    {
        storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageConnectionString"));

        CloudBlobClient client = storageAccount.CreateCloudBlobClient();

        CloudBlobContainer container = client.GetContainerReference("images");

        CloudBlockBlob blockBlob = container.GetBlockBlobReference(Path.GetFileName(path));

        using (MemoryStream ms = new MemoryStream())
        {
            blockBlob.DownloadToStream(ms);

            HttpContext.Current.Response.ContentType = blockBlob.Properties.ContentType.ToString();
            HttpContext.Current.Response.AddHeader("Content-Disposition", "Attachment; filename=" + Path.GetFileName(path).ToString());
            HttpContext.Current.Response.AddHeader("Content-Length", blockBlob.Properties.Length.ToString());
            HttpContext.Current.Response.BinaryWrite(ms.ToArray());
            HttpContext.Current.Response.Flush();
            HttpContext.Current.Response.Close();
        }
    }

Upvotes: 0

Stephen Cleary
Stephen Cleary

Reputation: 457362

the following throws an exception within HttpBaseStream

It looks like the HTTP result type is attempting to set the Content-Length header and is reading Length to do so. That would be the natural thing to do. However, it would also be natural to handle the NotSupportedException and just not set Content-Length at all.

If the NotSupportedException only shows up when running in the debugger, then just ignore it.

If the exception is actually thrown to your code (i.e., causing the request to fail), then you'll need to follow the rest of this answer.

First, create a minimal reproducible example and report a bug to the .NET team.

To work around this issue in the meantime, I recommend writing a stream wrapper that returns an already-determined length, which you can get from the Azure blob attributes. E.g.:

public sealed class KnownLengthStreamWrapper : Stream
{
  private readonly Stream _stream;
  public KnownLengthStreamWrapper(Stream stream, long length)
  {
    _stream = stream;
    Length = length;
  }
  public override long Length { get; private set; }
  ... // override all other Stream members and forward to _stream.
}

That should be sufficient to get your app working.

I tried to push the stream into an MemoryStream

This didn't work because you'd need to "rewind" the MemoryStream at some point, e.g.:

var stream = new MemoryStream();
await blobClient.DownloadToAsync(stream);
stream.Position = 0;
return stream;

Upvotes: 2

Related Questions