Skip to content

Instantly share code, notes, and snippets.

@chgeuer
Created September 2, 2021 06:36
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chgeuer/d2d5d214783b67f9c23a3b53ea96363c to your computer and use it in GitHub Desktop.
Save chgeuer/d2d5d214783b67f9c23a3b53ea96363c to your computer and use it in GitHub Desktop.
A gazillion ways to download a blob
using System;
using System.IO;
using System.Threading.Tasks;
using Azure;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
static class Program
{
// https://github.com/Azure/azure-sdk-for-net/issues/22022
static async Task Main(string[] args)
{
BlobClient b = new(new($"https://typora.blob.core.windows.net/typoraimages/2021/09/02/06/34/2021-08-30--08-48-47----74FZ3X4H2A2TK2Y5T2Z4XA06YC.png"));
var algos = new (string, Func<BlobClient, Task<byte[]>>)[] {
(nameof(DownloadStreamingAsync), DownloadStreamingAsync),
(nameof(DownloadAsync), DownloadAsync),
(nameof(DownloadToAsync), DownloadToAsync),
(nameof(DownloadContentAsync), DownloadContentAsync),
(nameof(OpenReadAsync), OpenReadAsync)
};
foreach ((var name, var algo) in algos)
{
var buffer = await algo(b);
Console.Error.WriteLine($"{name}: {buffer.Length}");
}
}
public static async Task<byte[]> DownloadStreamingAsync(this BlobClient blobClient)
{
// DownloadStreamingAsync gives you a network stream (wrapped with few layers but effectively think about it as network stream)
// which holds on to single connection.
// Use to access network stream directly for any advanced scenario
Response<BlobDownloadStreamingResult> response = await blobClient.DownloadStreamingAsync();
BlobDownloadStreamingResult value = response.Value;
Stream content = value.Content;
MemoryStream ms = new();
await content.CopyToAsync(ms);
return ms.ToArray();
}
public static async Task<byte[]> DownloadAsync(this BlobClient blobClient)
{
// DownloadAsync() has been deprecated.
BlobDownloadInfo value = (await blobClient.DownloadAsync()).Value;
Stream content = value.Content;
bool doItEasy = true;
if (doItEasy)
{
MemoryStream ms = new();
await content.CopyToAsync(ms);
return ms.ToArray();
}
else
{
int contentLength = (int)value.ContentLength;
byte[] buffer = new byte[contentLength];
int current = 0;
int read = await content.ReadAsync(buffer.AsMemory(current, (int)value.ContentLength - current));
while (read > 0)
{
current += read;
read = await content.ReadAsync(buffer.AsMemory(current, (int)value.ContentLength - current));
}
return buffer;
}
}
public static async Task<byte[]> DownloadToAsync(this BlobClient blobClient)
{
// This copies the blob into a stream, however, the SDK sets an
// "x-ms-range: bytes=0-268435455", i.e. only fetches the first 256 MB,
// no matter how large the blob is?!?
MemoryStream ms = new();
_ = await blobClient.DownloadToAsync(ms);
return ms.ToArray();
}
public static async Task<byte[]> DownloadContentAsync(this BlobClient blobClient)
{
// DownloadContentAsync() for downloading small blobs that can fit into memory
BlobDownloadResult value = (await blobClient.DownloadContentAsync()).Value;
BinaryData content = value.Content;
return content.ToArray();
}
public static async Task<byte[]> OpenReadAsync(this BlobClient blobClient)
{
// OpenReadAsync fetches payload in chunks and buffers issuing multiple requests to fetch content.
const int chunkSize = 8 * 1024 * 1024; // is 4MB by default
Stream stream = await blobClient.OpenReadAsync(options: new (allowModifications: true) { BufferSize = chunkSize });
MemoryStream ms = new();
await stream.CopyToAsync(ms);
return ms.ToArray();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment