Azure函数从Blob存储下载文件时遇认证错误求助
Azure Function中使用Azure.Storage.Blobs下载Blob时的认证错误问题
在Http触发的Azure Function中,使用Azure.Storage.Blobs库从Blob存储下载文件时,持续遇到认证错误,错误信息如下:
Server failed to authenticate the request. Make sure the value of the Authorization header is formed correctly including the signature
已验证连接字符串、账户密钥正确,且存储账户具备必要权限,但问题依旧。
相关代码
BlobStorage.cs
using Azure.Storage.Blobs; using Microsoft.Extensions.Logging; using System.Collections.Generic; using System; using System.IO; using System.Text; using System.Threading.Tasks; using Microsoft.Azure.Storage.Blob; using System.IO.Compression; public class BlobStorage : IBlobStorage { private readonly ILogger<BlobStorage> _logger; private readonly BlobServiceClient _blobServiceClient; public BlobStorage(ILogger<BlobStorage> logger, BlobServiceClient blobServiceClient) { _logger = logger; _blobServiceClient = blobServiceClient; } public async Task<byte[]> GetBlobFilesAsZipAsync(string containerName, int days) { _logger.LogInformation("{class} -> {method} -> Start", nameof(BlobStorage), nameof(BlobStorage.GetBlobFilesAsZipAsync)); await CreateContainerIfNotExistsAsync(containerName); var containerClient = _blobServiceClient.GetBlobContainerClient(containerName); var blobs = new List<CloudBlockBlob>(); var resultSegment = containerClient.GetBlobsAsync().AsPages().GetAsyncEnumerator(); while (await resultSegment.MoveNextAsync()) { var blobPage = resultSegment.Current; foreach (var blobItem in blobPage.Values) { var blobClient = containerClient.GetBlobClient(blobItem.Name); var blobProperties = await blobClient.GetPropertiesAsync(); var lastModified = blobProperties.Value.LastModified.UtcDateTime; //if ((DateTime.UtcNow - lastModified).TotalDays <= days) //{ var cloudBlockBlob = new CloudBlockBlob(blobClient.Uri); blobs.Add(cloudBlockBlob); //} } } _logger.LogInformation("{class} -> {method} -> End", nameof(BlobStorage), nameof(BlobStorage.GetBlobFilesAsZipAsync)); return await CreateZipFile(blobs); ; } private async Task CreateContainerIfNotExistsAsync(string containerName) { var containerClient = _blobServiceClient.GetBlobContainerClient(containerName); if (!await containerClient.ExistsAsync()) { await containerClient.CreateAsync(); _logger.LogInformation("Container {containerName} created if not exists.", containerName); } } /// <summary> /// CreateZipFile /// </summary> /// <param name="cloudBlockBlobs"></param> /// <returns></returns> private async Task<byte[]> CreateZipFile(IEnumerable<CloudBlockBlob> cloudBlockBlobs) { using (MemoryStream ms = new MemoryStream()) { using (ZipArchive archive = new ZipArchive(ms, ZipArchiveMode.Update, true)) { foreach (var file in cloudBlockBlobs) { ZipArchiveEntry entry = archive.CreateEntry(file.Name); using (Stream zipEntryStream = entry.Open()) using (MemoryStream blobStream = new MemoryStream()) { await file.DownloadToStreamAsync(blobStream); blobStream.Seek(0, SeekOrigin.Begin); await blobStream.CopyToAsync(zipEntryStream); } } } return ms.ToArray(); } } }
local.settings.json
{ "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "UseDevelopmentStorage=true", "FUNCTIONS_WORKER_RUNTIME": "dotnet" } }
Startup.cs
builder.Services.AddSingleton(x => new BlobServiceClient(connectionString: Environment.GetEnvironmentVariable("AzureWebJobsStorage"))); builder.Services.AddSingleton<IBlobStorage, BlobStorage>();
问题原因
代码中混合使用了新旧两个Azure Blob存储SDK:
- 新SDK:
Azure.Storage.Blobs(通过BlobServiceClient、BlobContainerClient、BlobClient操作) - 旧SDK:
Microsoft.Azure.Storage.Blob(使用CloudBlockBlob)
创建CloudBlockBlob时仅传入了Blob的Uri,未携带任何认证凭据(如连接字符串、SAS令牌),导致调用DownloadToStreamAsync时无法生成有效的Authorization请求头,触发认证错误。
解决方案
1. 统一使用新SDK
移除对Microsoft.Azure.Storage.Blob NuGet包的引用,全程使用Azure.Storage.Blobs提供的API。
2. 修改代码逻辑
直接使用BlobClient处理Blob下载,它继承了BlobServiceClient的认证信息,无需额外配置凭据。修改后的关键代码如下:
// 修改GetBlobFilesAsZipAsync方法的集合类型 public async Task<byte[]> GetBlobFilesAsZipAsync(string containerName, int days) { _logger.LogInformation("{class} -> {method} -> Start", nameof(BlobStorage), nameof(BlobStorage.GetBlobFilesAsZipAsync)); await CreateContainerIfNotExistsAsync(containerName); var containerClient = _blobServiceClient.GetBlobContainerClient(containerName); var blobs = new List<BlobClient>(); // 改为BlobClient集合 var resultSegment = containerClient.GetBlobsAsync().AsPages().GetAsyncEnumerator(); while (await resultSegment.MoveNextAsync()) { var blobPage = resultSegment.Current; foreach (var blobItem in blobPage.Values) { var blobClient = containerClient.GetBlobClient(blobItem.Name); var blobProperties = await blobClient.GetPropertiesAsync(); var lastModified = blobProperties.Value.LastModified.UtcDateTime; //if ((DateTime.UtcNow - lastModified).TotalDays <= days) //{ blobs.Add(blobClient); // 直接添加BlobClient //} } } _logger.LogInformation("{class} -> {method} -> End", nameof(BlobStorage), nameof(BlobStorage.GetBlobFilesAsZipAsync)); return await CreateZipFile(blobs); } // 修改CreateZipFile方法的参数和实现 private async Task<byte[]> CreateZipFile(IEnumerable<BlobClient> blobClients) { using (MemoryStream ms = new MemoryStream()) { using (ZipArchive archive = new ZipArchive(ms, ZipArchiveMode.Update, true)) { foreach (var blobClient in blobClients) { ZipArchiveEntry entry = archive.CreateEntry(blobClient.Name); using (Stream zipEntryStream = entry.Open()) { // 直接用BlobClient的DownloadToAsync方法写入压缩流 await blobClient.DownloadToAsync(zipEntryStream); } } } return ms.ToArray(); } }
3. 验证本地存储配置
如果使用本地模拟器,确保Azurite(或Azure Storage Emulator)已正常启动,local.settings.json中的AzureWebJobsStorage配置为UseDevelopmentStorage=true无误。
内容的提问来源于stack exchange,提问作者Rakesh Kumar
相关产品推荐
相关产品推荐

