Optimizing Azure Blob Performance with OpenReadAsync & Chunked Download

When optimizing Azure Blob Storage reading performance, two common approaches are,

  • Using OpenReadAsync(): Provides a stream for sequential reading.
  • Using Chunked Download: Downloads in parallel chunks for faster reads.

1. Using OpenReadAsync() (Efficient for Sequential Read)

The OpenReadAsync() method provides a streaming approach, allowing you to read data sequentially without loading the entire file into memory.

using System;
using System.IO;
using System.Threading.Tasks;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;

class Program
{
    private const string connectionString = "Your_Azure_Storage_Connection_String";
    private const string containerName = "your-container-name";
    private const string blobName = "largefile.dat"; // Blob to read

    static async Task Main()
    {
        await ReadBlobUsingStreamAsync();
    }

    private static async Task ReadBlobUsingStreamAsync()
    {
        BlobClient blobClient = new BlobClient(connectionString, containerName, blobName);

        Console.WriteLine("Reading blob using OpenReadAsync...");

        using (Stream blobStream = await blobClient.OpenReadAsync())
        using (FileStream fileStream = File.Create($"C:\\DownloadedBlobs\\{blobName}"))
        {
            await blobStream.CopyToAsync(fileStream);
        }

        Console.WriteLine("Download completed using OpenReadAsync.");
    }
}

Pros

  • Memory-efficient (reads sequentially, no need to load entire blob).
  • Best for streaming (e.g., video/audio files, log processing).
  • Ideal for large files (without consuming too much RAM).

Cons

  • Slower for large files (compared to chunked downloads).
  • No parallelization (reads sequentially).

2. Chunked Download (Parallelized for Speed)

For large blobs, downloading in chunks can improve performance by leveraging parallel reads.

Example Code for Chunked Download

using System;
using System.IO;
using System.Threading.Tasks;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;

class Program
{
    private const string connectionString = "Your_Azure_Storage_Connection_String";
    private const string containerName = "your-container-name";
    private const string blobName = "largefile.dat";
    private const int chunkSize = 4 * 1024 * 1024; // 4 MB chunks

    static async Task Main()
    {
        await DownloadBlobInChunksAsync();
    }

    private static async Task DownloadBlobInChunksAsync()
    {
        BlobClient blobClient = new BlobClient(connectionString, containerName, blobName);
        BlobProperties properties = await blobClient.GetPropertiesAsync();
        long totalSize = properties.ContentLength;

        Console.WriteLine($"Downloading blob in chunks... Total Size: {totalSize} bytes");

        using (FileStream fileStream = new FileStream($"C:\\DownloadedBlobs\\{blobName}", FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: chunkSize, useAsync: true))
        {
            long offset = 0;
            var tasks = new Task[(int)Math.Ceiling((double)totalSize / chunkSize)];

            for (int i = 0; i < tasks.Length; i++)
            {
                long currentOffset = offset;
                int currentChunkSize = (int)Math.Min(chunkSize, totalSize - currentOffset);

                tasks[i] = Task.Run(async () =>
                {
                    byte[] buffer = new byte[currentChunkSize];
                    await blobClient.DownloadToAsync(new MemoryStream(buffer), new Azure.HttpRange(currentOffset, currentChunkSize));
                    lock (fileStream) fileStream.Seek(currentOffset, SeekOrigin.Begin);
                    await fileStream.WriteAsync(buffer, 0, buffer.Length);
                });

                offset += currentChunkSize;
            }

            await Task.WhenAll(tasks);
        }

        Console.WriteLine("Download completed using chunked download.");
    }
}

Pros

  • Faster performance (downloads multiple parts in parallel).
  • Efficient for large blobs (GB/TB-sized files).
  • Can resume failed downloads by tracking completed chunks.

Cons

  • Higher memory usage (since multiple chunks are loaded in memory).
  • Complexity (managing threads and handling failures).

Which One to Choose?

Use OpenReadAsync() when

  • You need sequential reads (e.g., log processing, streaming).
  • Memory usage should be minimal.
  • The file size is small to medium (~100MB or less).
  • Parallelism is not required.

Use Chunked Download when

  • You need faster downloads for large files (100MB+ to GB/TB scale).
  • Parallel downloads can improve performance.
  • You need the ability to resume downloads after failure.
  • More memory consumption is acceptable for speed improvements.

If performance is the priority, chunked downloads are the way to go. If you only need to read a file sequentially, then OpenReadAsync() is the best approach.

Up Next
    Ebook Download
    View all
    Learn
    View all