Integrating Azure Blob Storage with a .NET 10 Web API

Modern apps work with files all the time—PDFs, images, invoices, contracts, and more. Azure Blob Storage is a great choice for storing these files because it’s secure and scales easily, and .NET 10 makes building the API simple and fast.

In this post, we’ll create a .NET 10 Web API that lets you upload multiple documents to Azure Blob Storage.

Architecture Overview

Flow

  • A client (Angular, React, Postman, etc.) uploads multiple files

  • The .NET 10 Web API receives the files using multipart/form-data

  • The API streams the files to Azure Blob Storage

  • The API returns blob URLs (or file metadata) back to the client

Tech Stack

  • .NET 10 Web API

  • Azure Blob Storage

  • Azure.Storage.Blobs SDK

Note: In this blog, I’ll focus only on the API side.

The source code can be downloaded from GitHub

Step 1: Create Azure Blob Storage

  1. Create a Storage Account in Azure

  2. Create a Blob Container

    • Example: documents

    • Access level: Private (recommended)

Grab:

  • Connection String

  • Container Name

Step 2: Install Required NuGet Package

Create a .NET Web API project in .NET 10 and install the below package.

dotnet add package Azure.Storage.Blobs

Step 3: Configure App Settings

Add the below settings into appsettings.json

{
  "AzureBlobStorage": {
    "ConnectionString": "DefaultEndpointsProtocol=...",
    "ContainerName": "documents"
  }
}

Step 4: Blob Storage Service using Dependency Injection

4.1 Create a Configure Options Class

 -  create a record as below

public record AzureBlobOptions
{
    public string ConnectionString { get; init; } = string.Empty;
    public string ContainerName { get; init; } = string.Empty;
}

4.2 Register Blob Client + Options in Program.cs

builder.Services.Configure<AzureBlobOptions>(
    builder.Configuration.GetSection("AzureBlobStorage"));

builder.Services.AddSingleton(sp =>
{
    var options = sp.GetRequiredService<IOptions<AzureBlobOptions>>().Value;

    return new BlobContainerClient(
        options.ConnectionString,
        options.ContainerName);
});

Note: BlobContainerClient is thread-safe → Singleton is recommended.

4.3. Create an Interface and Implementation

This keep our controller clean and testable

public interface IBlobStorageService
{
    Task<List<string>> UploadFilesAsync(List<IFormFile> files);
}

Implementation

using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;

public class BlobStorageService : IBlobStorageService
{
    private readonly BlobContainerClient _containerClient;

    public BlobStorageService(BlobContainerClient containerClient)
    {
        _containerClient = containerClient;
    }

    public async Task<List<string>> UploadFilesAsync(List<IFormFile> files)
    {
        var uploadedUrls = new List<string>();

        await _containerClient.CreateIfNotExistsAsync(PublicAccessType.None);

        foreach (var file in files)
        {
            if (file.Length <= 0)
                continue;

            var blobName = $"{Guid.NewGuid()}-{file.FileName}";
            var blobClient = _containerClient.GetBlobClient(blobName);

            await using var stream = file.OpenReadStream();

            await blobClient.UploadAsync(
                stream,
                new BlobHttpHeaders { ContentType = file.ContentType });

            uploadedUrls.Add(blobClient.Uri.ToString());
        }

        return uploadedUrls;
    }
}

Step 5: Register the Service in Program.cs

builder.Services.AddScoped<IBlobStorageService, BlobStorageService>();

Step 6: Create the Upload API Endpoint

[ApiController]
[Route("api/documents")]
public class DocumentsController : ControllerBase
{
    private readonly IBlobStorageService _blobStorageService;

    public DocumentsController(IBlobStorageService blobStorageService)
    {
        _blobStorageService = blobStorageService;
    }

    [HttpPost("upload")]
    [RequestSizeLimit(100_000_000)] // 100 MB
    public async Task<IActionResult> UploadDocuments([FromForm] List<IFormFile> files)
    {
        if (files == null || files.Count == 0)
            return BadRequest("No files uploaded.");

        var urls = await _blobStorageService.UploadFilesAsync(files);

        return Ok(new
        {
            Count = urls.Count,
            Files = urls
        });
    }
}

Optional Enhancements (Highly Recommended)

It is always good to incorporate the below

  • Validate file type

  • Virus scan (Azure Defender for Storage Or integrate with a scanning service before upload)

  • Use Managed Identity - Avoid connection string in production and use Azure Managed Identity or Default Credentials

 Conclusion

Connecting Azure Blob Storage with a .NET 10 Web API is simple if you keep the design clean and use dependency injection from the start. By streaming files straight from the API to Blob Storage, you avoid using too much memory and keep the solution scalable and ready for production.

In this post, we looked at how to accept multiple files using multipart/form-data, upload them efficiently to Azure Blob Storage, and organize the API using proper abstractions and dependency injection instead of tightly coupled code. This approach makes the code easier to test, maintain, and extend as your needs grow.

Note: This blog focuses only on the API side. Client-side uploads, UI details, and advanced security options like SAS tokens or Managed Identity can be added later if needed.

With this setup, you’re well prepared to handle document uploads in modern applications and can easily extend the solution to support metadata, validation, virus scanning, or secure downloads.

Happy Coding!