Upload/Get Blob - 在 .NET Core 中使用 Azure 存储帐户创建容器

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

我研究这个已经有一段时间了,但我一直无法从我的 .NET Core webapi 对我的存储帐户进行基本操作。

你应该知道以下内容:

  1. 允许 Blob public 访问设置为“已禁用”(强烈 微软推荐)
  2. 帐户类型是 BlobStorage
  3. 必须通过共享密钥授权对 Azure 存储的请求(我不喜欢 SAS)

我在我的项目中引用了包 Azure.Storage.Blobs 并将其作为服务注入,如下所示:

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

AzureBlobStorage 是我到存储帐户的连接字符串。

我还创建了一个 blob 服务,并将以下方法放入

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);

上传 blob 的过程如下:

  1. 我在 post man

    中点击了以下 http 请求(方法:POST)

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

唯一标识符是我存储帐户中的容器名称。

  1. 在我的控制器中,我有以下逻辑调用

     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. 调用命中我的 UploadBlobAsync 方法并传递参数..到目前为止一切顺利!

但是,我 运行 遇到了一个问题,我的代码抛出一个异常,指出 public 访问被禁止

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;
}

我的GetContainerClient()是一个私有方法,我用它来检查容器是否存在,如果不存在,则创建一个容器

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

到目前为止,我得到这个异常的原因很明显..因为 public 访问级别被禁用..

Microsoft 文档说我需要生成一个以某种方式附加到我的请求的共享访问密钥..

这就是为什么我有另一种方法可以生成适当的请求 headers,如下所示:

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;
 }

问题是我不确定在哪里注入那些请求 headers 以上传我的 blob 或创建容器,甚至从我的存储帐户中执行 get/list/delete blob?

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();
}

这应该很容易,但由于某种原因,我已经卡在这几天了..

您的 blob 存储帐户设置为:

Allow Blob public access is set to "Disabled"

然后你的代码通过 PublicAccessType.Blob:

containerClient.CreateIfNotExists(PublicAccessType.Blob);

然后你得到一个异常:

throws an exception saying that public access is prohibited

在我看来解决方法是:

  1. 删除 blob 的 public 访问权限:containerClient.CreateIfNotExists();,或
  2. Enable blob public access for the storage account.

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