简体   繁体   中英

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..

Your blob storage account is set up with:

Allow Blob public access is set to "Disabled"

And then your code passes PublicAccessType.Blob :

containerClient.CreateIfNotExists(PublicAccessType.Blob);

And then you get an exception:

throws an exception saying that public access is prohibited

Sounds to me like the fix is to either:

  1. Remove the public access for the blob: containerClient.CreateIfNotExists(); , or
  2. Enable blob public access for the storage account .

As documented :

To grant anonymous users read access to a container and its blobs, first allow public access for the storage account, then set the container's public access level. If public access is denied for the storage account, you will not be able to configure public access for a container.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM