2021年4月30日星期五

Upload/Get Blob - Create Container with Azure Storage Account in .NET Core

I have been researching this for quite some time and I am stuck at being able to do basic operations on my storage account from my .NET Core webapi.

You should know the following:

  1. Allow Blob public access is set to "Disabled" (Strongly recommended by Microsoft)
  2. Account type is BlobStorage
  3. Authorize requests to Azure Storage must be done through a shared key (I do not prefer SAS)

I have referenced the package Azure.Storage.Blobs in my project and injected it as a service like the following:

services.AddSingleton(x => new BlobServiceClient(config.GetValue<string>("AzureBlobStorage")));  

The AzureBlobStorage is my connection string to the storage account.

I also created a blob service and I put the following methods in

Task<Uri> UploadBlobAsync(string blobContainerName, Stream content, string contentType, string fileName);  Task<CustomBlobInfo> GetBlobAsync(string blobContainerName, string fileName);  Task<IEnumerable<string>> ListBlobsAsync(string blobContainerName);  Task DeleteBlobAsync(string blobContainerName, string fileName);  

The process of uploading a blob starts as the following:

  1. I hit the following http request (Method:POST) in my post man

    http://localhost:5000/api/blob/container/6735D6F0-8FC1-4C6E-B08C-FFBB5B29C4A1

The uniqueidentifier is the container name in my storage account.

  1. In my controller, I have the following logic that calls

     IFormFile file = Request.Form.Files[0];   if (file == null)   {        return BadRequest();   }     string fileName = Guid.NewGuid().ToString().ToLower();   string containerName = blobContainerName.ToLower();       var result = await this.blobService.UploadBlobAsync(                  containerName,                  file.OpenReadStream(),                  file.ContentType,                  fileName);   var toReturn = result.AbsoluteUri;     return Ok(new { path = toReturn });  
  2. The call hits my UploadBlobAsync method and pass the arguments.. so far so good!

However, I run into a problem where my code throws an exception saying that public access is prohibited

public async Task<Uri> UploadBlobAsync(string blobContainerName, Stream content, string contentType, string fileName)  {      var containerClient = GetContainerClient(blobContainerName); // <<<<< EXCEPTION      var blobClient = containerClient.GetBlobClient(fileName);      await blobClient.UploadAsync(content, new BlobHttpHeaders { ContentType = contentType });      return blobClient.Uri;  }  

My GetContainerClient() is a private method that I use to check if the container exists or not, if it doesn't, then create a container

private BlobContainerClient GetContainerClient(string blobContainerName)  {        var containerClient = this.blobServiceClient.GetBlobContainerClient(blobContainerName);        containerClient.CreateIfNotExists(PublicAccessType.Blob);        return containerClient;  }  

So far, it is obvious why I am getting this exception.. because the public access level is disabled..

Microsoft documentation says that I need to generate a shared access key that somehow gets attached to my request..

This is why I have another method that generate the appropriate request headers as the following:

private void GenerateSharedAccessKeyRequestHeader(string blobContainerName, Stream content, string fileName, string contentType)   {        string storageKey = "key";      string storageAccount = "name";            string method = "PUT";      long contentLength = content.Length;        string requestUri = $"https://{storageAccount}.blob.core.windows.net/{blobContainerName}/{fileName}";        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUri);        string now = DateTime.UtcNow.ToString("R");        request.Method = method;      request.ContentType = contentType;      request.ContentLength = contentLength;        request.Headers.Add("x-ms-version", "2015-12-11");      request.Headers.Add("x-ms-date", now);      request.Headers.Add("x-ms-blob-type", "BlockBlob");      request.Headers.Add("Authorization", AuthorizationHeader(method, now, request, storageAccount, storageKey, blobContainerName, fileName));  }    private string AuthorizationHeader(string method, string now, HttpWebRequest request, string storageAccount, string storageKey, string containerName, string blobName)   {        string headerResource = $"x-ms-blob-type:BlockBlob\nx-ms-date:{now}\nx-ms-version:2015-12-11";      string urlResource = $"/{storageAccount}/{containerName}/{blobName}";      string stringToSign = $"{method}\n\n\n{request.ContentLength}\n\n{request.ContentType}\n\n\n\n\n\n\n{headerResource}\n{urlResource}";        HMACSHA256 hmac = new HMACSHA256(Convert.FromBase64String(storageKey));      string signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));        String AuthorizationHeader = String.Format("{0} {1}:{2}", "SharedKey", storageAccount, signature);      return AuthorizationHeader;   }  

The problem is I am not sure where to inject those request headers to upload my blob or to create container or even to do get/list/delete a blob from my storage account?

public async Task<CustomBlobInfo> GetBlobAsync(string blobContainerName, string fileName)  {      var containerClient = GetContainerClient(blobContainerName);      var blobClient = containerClient.GetBlobClient(fileName);      var blobDownloadInfo = await blobClient.DownloadAsync();      return new CustomBlobInfo(blobDownloadInfo.Value.Content, blobDownloadInfo.Value.ContentType);  }    public async Task<IEnumerable<string>> ListBlobsAsync(string blobContainerName)  {      var containerClient = GetContainerClient(blobContainerName);      var items = new List<string>();      await foreach (var item in containerClient.GetBlobsAsync())      {          items.Add(item.Name);      }      return items;  }        public async Task DeleteBlobAsync(string blobContainerName, string fileName)  {      var containerClient = GetContainerClient(blobContainerName);      var blobClient = containerClient.GetBlobClient(fileName);      await blobClient.DeleteIfExistsAsync();  }  

This should be easy but for some reason, I have been stuck in this for days..

https://stackoverflow.com/questions/67341436/upload-get-blob-create-container-with-azure-storage-account-in-net-core May 01, 2021 at 07:12AM

没有评论:

发表评论