6

I am using Azure.Storage.Blobs version=12.4.1. I have a REST endpoint that I want to use to download blobs from a storage account.

I need to stream the result to a HttpResponseMessage and I do not want to use a MemoryStream. I want to stream the result directly to the calling client. Is there a way to achieve this. How to get the downloaded blob in the HttpResponseMessage content? I do not want to use MemoryStream, since there will be a lot of download requests.

The BlobClient class has a method DownloadToAsync but it requires a Stream as a parameter.

        var result = new HttpResponseMessage(HttpStatusCode.OK);

        var blobClient = container.GetBlobClient(blobPath);
        if (await blobClient.ExistsAsync())
        {
            var blobProperties = await blobClient.GetPropertiesAsync();

            var fileFromStorage = new BlobResponse()
            {                    
                ContentType = blobProperties.Value.ContentType,
                ContentMd5 = blobProperties.Value.ContentHash.ToString(),
                Status = Status.Ok,
                StatusText = "File retrieved from blob"
            };

            await blobClient.DownloadToAsync(/*what to put here*/);
            return fileFromStorage;
        }
Angela
  • 477
  • 1
  • 10
  • 20
  • I am currently using Azure.Storage.Blobs v12.11.0 and BlobClient has OpenRead and OpenReadAsync methods, which you can use. Also, your code is using methods like `CreateCloudBlobClient`, which is for Legacy Azure SDK for .NET. I'll provide a more updated solution in case it is helpful for anyone using the latest Azure SDK for .NET and a later version of Azure.Storage.Blobs v12. – kimbaudi Apr 29 '22 at 16:00

4 Answers4

10

You could simply create a new memory stream and download the blob's content to that stream.

Something like:

        var connectionString = "UseDevelopmentStorage=true";
        var blobClient = new BlockBlobClient(connectionString, "test", "test.txt");
        var ms = new MemoryStream();
        await blobClient.DownloadToAsync(ms);

ms will have the blob's contents. Don't forget to reset memory stream's position to 0 before using it.

Gaurav Mantri
  • 128,066
  • 12
  • 206
  • 241
  • 1
    I want to avoid using MemoryStream, I do not want to have the blob content in memory – Angela May 07 '20 at 09:17
  • It's not very clear what exactly do you want. This method needs a stream. If memory stream is not an option then you may want to save to a file? – Gaurav Mantri May 07 '20 at 09:19
  • Other options would be to use `DownloadAsync` method which returns a `BlobDownloadInfo`. You can pass that object to the client directly. – Gaurav Mantri May 07 '20 at 09:22
  • 3
    What is the v12 replacement for DownloadTextAsync? That was such a useful method to load templates stored in blob storage. – dinotom Jul 29 '21 at 11:12
  • Hi, @dinotom, have you found the DownloadTextAsync replacement? – Smit Jan 03 '22 at 10:34
  • I understood, the right replacement is to use the `DownloadToAsync` method where we got the `MemoryStream` and then can create a `StreamReader` based on the memory stream and utilize the `ReadToEndAsync` method. – Smit Jan 04 '22 at 02:20
  • This is an outdated solution that works for Legacy Azure SDK for .NET, but not the latest Azure SDK for .NET using Azure.Storage.Blobs v12.11.0 for example. – kimbaudi Apr 29 '22 at 16:02
1

You need to use

 BlobDownloadInfo download = await blobClient.DownloadAsync();

download.Content is blob Stream. You can use it to copy directly to other stream.

using (var fileStream = File.OpenWrite(@"C:\data\blob.bin"))
{
    await download.CopyToAsync(fileStream);
}
Piotr Perak
  • 10,718
  • 9
  • 49
  • 86
  • 1
    I think this is no longer an option: https://learn.microsoft.com/en-us/dotnet/api/azure.storage.blobs.models.blobdownloadinfo?view=azure-dotnet – Todd Ropog Jan 29 '21 at 23:41
0

To download a blob using Azure.Storage.Blobs v12.11.0, we can use OpenReadAsync or OpenRead

string connectionString = Environment.GetEnvironmentVariable("AZURE_STORAGE_CONNECTION_STRING")!;
var serviceClient = new BlobServiceClient(connectionString);
var container = serviceClient.GetBlobContainerClient("myblobcontainername");

string blobName; // the name of the blob
var client = container.GetBlobClient(HttpUtility.UrlDecode(blobName));
var properties = (await client.GetPropertiesAsync()).Value;
Response.Headers.Add("Content-Disposition", $"attachment; filename={Path.GetFileName(client.Name)}");
Response.Headers.Add("Content-Length", $"{properties.ContentLength}");
return File(await client.OpenReadAsync(), properties.ContentType);
kimbaudi
  • 13,655
  • 9
  • 62
  • 74
-1

Try to use the code as below to download blob into HttpResponseMessage.

try
{
    var storageAccount = CloudStorageAccount.Parse("{connection string}");
    var blobClient = storageAccount.CreateCloudBlobClient();
    var Blob = await blobClient.GetBlobReferenceFromServerAsync(new Uri("https://{storageaccount}.blob.core.windows.net/{mycontainer}/{blobname.txt}"));
    var isExist = await Blob.ExistsAsync();
    if (!isExist) {
        return Request.CreateErrorResponse(HttpStatusCode.NotFound, "file not found");
    }
    HttpResponseMessage message = new HttpResponseMessage(HttpStatusCode.OK);
    Stream blobStream = await Blob.OpenReadAsync();
    message.Content = new StreamContent(blobStream);
    message.Content.Headers.ContentLength = Blob.Properties.Length;
    message.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(Blob.Properties.ContentType);
    message.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
    {
        FileName = "{blobname.txt}",
        Size = Blob.Properties.Length
    };
    return message;
}
catch (Exception ex)
{
    return new HttpResponseMessage
    {
        StatusCode = HttpStatusCode.InternalServerError,
        Content = new StringContent(ex.Message)
    };
}
Joey Cai
  • 18,968
  • 1
  • 20
  • 30
  • This is what I used to have, but now I need to use Azure.Storage.Blobs version=12.4.1 and BlobClient that does not have OpenReadAsync – Angela May 07 '20 at 09:09
  • Same problem. It would be nice if this reflected the latest Azure.Storage.Blogs library which is at v12. – FlyingMaverick Jul 16 '20 at 14:48
  • Azure.Storage.Blobs v12.11.0 has [`OpenReadAsync`](https://learn.microsoft.com/en-us/dotnet/api/azure.storage.blobs.specialized.blobbaseclient.openreadasync) or [`OpenRead`](https://learn.microsoft.com/en-us/dotnet/api/azure.storage.blobs.specialized.blobbaseclient.openread) so it looks like Microsoft brought it back – kimbaudi Apr 29 '22 at 16:16