上传/获取 Blob - 在 .NET Core 中使用 Azure 存储帐户创建容器

问题描述

我对此进行了相当长的研究,但一直无法通过 .NET Core webapi 对存储帐户执行基本操作。

您应该了解以下内容

  1. 允许 Blob 公共访问设置为“已禁用”(强烈 微软推荐)
  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

uniqueidentifier 是我的存储帐户中的容器名称

  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 async Task<Uri> UploadBlobAsync(string blobContainerName,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;
}

到目前为止,很明显为什么我会收到此异常..因为公共访问级别已禁用..

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

这就是为什么我有另一种方法生成相应的请求标头,如下所示:

private void GenerateSharedAccessKeyRequestHeader(string blobContainerName,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",signature);
    return AuthorizationHeader;
 }

问题是我不确定在哪里注入这些请求标头以上传我的 blob 或创建容器,甚至从我的存储帐户中获取/列出/删除 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 存储帐户设置为:

允许 Blob 公共访问设置为“已禁用”

然后您的代码通过 PublicAccessType.Blob

containerClient.CreateIfNotExists(PublicAccessType.Blob);

然后你得到一个例外:

抛出异常,表示禁止公开访问

在我看来,修复方法是:

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

作为documented

要授予匿名用户对容器及其 ​​Blob 的读取访问权限,请首先允许对存储帐户进行公共访问,然后设置容器的公共访问级别。如果存储帐户的公共访问被拒绝,您将无法为容器配置公共访问。