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:
- Clients for blob specific CRUD operations:
- Azure.Storage.Blobs.Specialized Namespace
Uploading an image
Scenario:
- User can upload image for a product (from product page of WebApp)
- There is a product API controller (API service is used by WebApp)
- Uploaded image will be assigned to the product of given id
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:
- Nuget package and API might be changed
- Check microsoft docs for latest updates and implement accordingly
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
- SAS => Shared Access Signature
- Go to blob storage > select blob and get URI
- Use URI and SAS to get the blob
Notes:
- Nuget package and API might be changed
- Check microsoft docs for latest updates and implement accordingly
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:
- Nuget package and API might be changed
- Check microsoft docs for latest updates and implement accordingly
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();