Author : MD TAREQ HASSAN | Updated : 2020/10/22

Prerequisites

Dependency

Nuget

Install-Package Azure.Storage.Blobs

Setup

appsettings.json (best way: put connection string in Azure KeyVault, see: Using Azure KeyVault in Production)

{
  "az_storage_account_connection_string": "DefaultEndpointsProtocol=https;AccountName=...;AccountKey=...;EndpointSuffix=core.windows.net",
  "az_blob_storage_name": "images",
}

Constants.cs

public static class Constants
{
	public static readonly string KEY_AZURE_STORAGE_ACCOUNT_CONNECTION_STRING = "az_storage_account_connection_string";
	public static readonly string KEY_BLOB_STORAGE_CONTAINER_NAME = "az_blob_storage_name";
}

IBlobService.cs

public interface IBlobService
{
    Task<string> UploadBlobAsync(string blobName, Stream imageStream)
}

AzureBlobService.cs

using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using Microsoft.Extensions.Configuration;
using System.IO;
using System.Threading.Tasks;

namespace Foo.Services
{

    public class AzureBlobService : IBlobService
    {
        string azureStorageAccountConnectionString;
        string blobStorageContainerName;


        public AzureBlobService(IConfiguration configuration)
        {

            azureStorageAccountConnectionString = configuration[Constants.KEY_AZURE_STORAGE_ACCOUNT_CONNECTION_STRING];
            blobStorageContainerName = configuration[Constants.KEY_BLOB_STORAGE_CONTAINER_NAME];
        }

        public async Task<string> UploadBlobAsync(string blobName, Stream imageStream)
        {
            var blobContainerClient = new BlobContainerClient(azureStorageAccountConnectionString, blobStorageContainerName);

            var blobClient = blobContainerClient.GetBlobClient(blobName);

            var blobHttpHeaders = new BlobHttpHeaders
            {
                ContentType = "image/jpeg",
                CacheControl = "public"
            };

            await blobClient.UploadAsync(imageStream, blobHttpHeaders);

            return blobClient.Uri.ToString();
        }
    }
}

Blob specific clients

SDK (Azure.Storage.Blobs.Specialized Namespace) have blob type specific clients:

Uploading an image

Scenario:

ProductController.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Foo.Models;
using Foo.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace Foo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ProductController : ControllerBase
    {
        IBlobService blobStorageService;

        public ProductController(IBlobService blobStorageService)
        {
            this.blobStorageService = blobStorageService;
        }

        // ... ... ...
		
        [HttpPost]
        [Route("/api/[controller]/Image/{id}")]
        public async Task<IActionResult> AddImage(IFormFile imageFile)
        {
            if (!Request.HasFormContentType)
            {
                return new UnsupportedMediaTypeResult();
            }
			
            string id = (string)RouteData.Values["id"];

            var filename = $"{id}.jpg";

            try
            {
                using Stream imageStream = imageFile.OpenReadStream();
                
                var blobRef = await blobStorageService.UploadBlobAsync(blobName: filename, imageStream: imageStream);

                // update product table (in Azure SQL or Cosmos DB) with image link
            }
            catch(Exception ex)
            {
                // Do logging
				
                throw ex;
            }

            return Ok();
        }
    }
}

Uploading downloading and enumerating blobs

See: https://github.com/Azure/azure-sdk-for-net/tree/master/sdk/storage/Azure.Storage.Blobs#examples

Append blob

CloudBlobContainer cbc = Client.GetContainerReference("test-append-blob-container");
await cbc.CreateIfNotExistsAsync();

CloudAppendBlob blob = cbc.GetAppendBlobReference("test-append-blob.data");
await blob.CreateOrReplaceAsync();

for (int ix = 0; ix < 100; ix++)
{
	string txt =
		$"Line #{(ix + 1).ToString("d6")}, written at {DateTime.UtcNow.ToLongTimeString() }: " +
		string.Empty.PadRight(20, '*') +
		Environment.NewLine;

	await blob.AppendTextAsync(txt);
}

BlockBlob

Notes:

Upload

var Client = //...
var container = Client.GetContainerReference("test-blockblob-blockuploads");
await container.CreateIfNotExistsAsync();

var blob = container.GetBlockBlobReference("myblockblob");
await blob.DeleteIfExistsAsync();

var myDataBlockList = new List<string>();
myDataBlockList.Add(string.Empty.PadRight(50, '*') + Environment.NewLine);
myDataBlockList.Add(string.Empty.PadRight(50, '-') + Environment.NewLine);
myDataBlockList.Add(string.Empty.PadRight(50, '=') + Environment.NewLine);
myDataBlockList.Add(string.Empty.PadRight(50, '-') + Environment.NewLine);
myDataBlockList.Add(string.Empty.PadRight(50, '*') + Environment.NewLine);

var blockIDs = new List<string>();

int id = 0;
foreach (var myDataBlock in myDataBlockList)
{
	id++;
	var blockID = Convert.ToBase64String(
		Encoding.UTF8.GetBytes(
			id.ToString("d6")));

	var blockData =
		new MemoryStream(
			Encoding.UTF8.GetBytes(myDataBlock));

	await blob.PutBlockAsync(blockID, blockData, null);

	blockIDs.Add(blockID);
}
await blob.PutBlockListAsync(blockIDs);

//await blob.FetchAttributesAsync();
//var etag = blob.Properties.ETag;
//Check.That(etag).IsNotNull().And.IsNotEmpty();

Modify

var container = _Client.GetContainerReference("test-blockblob-blockuploads");
await container.CreateIfNotExistsAsync();

var blob = container.GetBlockBlobReference("myblockblob");
bool blobExists = await blob.ExistsAsync();
Check.That(blobExists).IsTrue();

var blockItems = await blob.DownloadBlockListAsync();
var blockIDs = from blockInfo in blockItems
			   select blockInfo.Name;

var myNewBlock = string.Empty.PadRight(50, '+') + Environment.NewLine;
var nBlockID = 3;
var blockID = Convert.ToBase64String(Encoding.UTF8.GetBytes((nBlockID.ToString("d6"))));
var blockData = new MemoryStream(Encoding.UTF8.GetBytes(myNewBlock));

Check.That(blockIDs).Contains(blockID);

await blob.PutBlockAsync(blockID, blockData, null);
await blob.PutBlockListAsync(blockIDs);

Accessing blob using Shared Access Signature

Notes:

Downloading blob using SAS

static string sasToken_Read = null;
		
var saName = "";
var saKey = "";
var saCreds = new StorageCredentials(saName, saKey);
var saConfig = new CloudStorageAccount(saCreds, true);

Client = saConfig.CreateCloudBlobClient();

var cr = Client.GetContainerReference("photos");
var br = cr.GetBlobReference("madagascar.jpg");
sasToken_Read = br.GetSharedAccessSignature(
	new SharedAccessBlobPolicy()
	{
		Permissions = SharedAccessBlobPermissions.Read,
		SharedAccessExpiryTime = new DateTimeOffset(DateTime.Now.AddMinutes(2))
	}
);

var blob = new CloudBlob(
	new Uri("https://learnazureblobstoday.blob.core.windows.net/photos/madagascar.jpg"),
	new StorageCredentials(_sasToken_Read)
	);

await blob.DownloadToStreamAsync(new MemoryStream());

Fetching Blob Metadata

Notes:

saName = "";
saKey = "";

var saCreds = new StorageCredentials(saName, saKey);
var saConfig = new CloudStorageAccount(saCreds, true);

var client = saConfig.CreateCloudBlobClient();

var myFirstContainerName = "photos";
var container = client.GetContainerReference(myFirstContainerName);
await container.CreateIfNotExistsAsync();

await container.FetchAttributesAsync();