我编写了一个 Azure 函数,用于在 HTTP 触发器上下载 Blob。当我在本地调试此函数时,代码执行会遍历整个函数,经过返回值,然后在实际发送返回值之前抛出一个模糊的错误。
这是函数:
public class GetBlob
{
private readonly IUpgradeDataRepositoryFactory _upgradeDataRepository;
private readonly IAuthorizationHelper _authorizationHelper;
public GetBlob(IUpgradeDataRepositoryFactory upgradeDataRepository, IAuthorizationHelper authorizationHelper){
_upgradeDataRepository = upgradeDataRepository;
_authorizationHelper = authorizationHelper;
}
[FunctionName("GetBlob")]
public HttpResponseMessage Run(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "GetBlob/{id}")] HttpRequest req,
string id, ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
var clientIp = (req.Headers["X-Forwarded-For"].FirstOrDefault() ?? "").Split(new char[] { ':' }).FirstOrDefault();
try{
_authorizationHelper.AssertRequestIsAuthorized(req);
var blobInfo = _upgradeDataRepository.UpdateDataItemRepository.Query(q => q.BlobName == id).FirstOrDefault();
if(blobInfo != null){
using (var blobStream = GetBlobStream(id))
{
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new StreamContent(blobStream);
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = blobInfo.BlobFriendlyName;
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
log.LogInformation(string.Format("Returning Update Blob: {0} to ip: {1}", id, clientIp));
return result;
}
}
else
{
log.LogInformation(string.Format("Could not find update item record for blob id: {0} Ip: {1}", id, clientIp));
return new HttpResponseMessage(HttpStatusCode.NotFound);
}
}
catch (AuthorizationException){
log.LogInformation("Authorization failed");
var message = new HttpResponseMessage()
{
StatusCode = HttpStatusCode.Unauthorized
};
throw new HttpResponseException(message);
}
catch (Exception ex){
log.LogError(string.Format("Error getting update blob: {0} for IP: {1}", id, clientIp));
log.LogError(ex.ToString());
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
finally{
log.LogInformation(string.Format("Exiting Get Update Blob: {0} for ip: {1}", id, clientIp));
}
}
private static CloudStorageAccount GetStorageAccount()
{
var connParameter= "StorageAccountConnectionString";
var connectionString = System.Environment.GetEnvironmentVariable($"{connParameter}");
return CloudStorageAccount.Parse(connectionString);
}
private static string GetContainerName(){
var containerParameter= "UpdateBlobContainerName";
var containerName = System.Environment.GetEnvironmentVariable($"{containerParameter}");
return containerName;
}
private static Stream GetBlobStream(string id)
{
CloudStorageAccount account = GetStorageAccount();
CloudBlobClient blobClient = account.CreateCloudBlobClient();
CloudBlobContainer container = blobClient.GetContainerReference(GetContainerName());
var blob = container.GetBlobReference(id);
return blob.OpenReadAsync().Result;
}
}
(注 1:这是使用 HttpResponseMessage,因为它正在替换旧的 API,并且需要与旧代码集成)
(注 2:虽然我还没有共享 AuthenticationHelper 或 UpgradeDataRepository 的代码,但这两个对象都已在单独的工作函数中成功使用和测试。UpgradeDataRepository 连接到 Azure SQL 数据库并查询一些信息 - 这种情况正在发生在工作函数和本函数中都成功。但是,工作函数不访问 blob 存储,因此错误一定与使用 blob 存储有关。)
这是终端输出:
[2024-03-27T19:04:34.288Z] Executing 'GetBlob' (Reason='This function was programmatically called via the host APIs.', Id=5b3af3ef-c61e-4ad7-a582-4982ba9d6b3e)
[2024-03-27T19:04:34.300Z] C# HTTP trigger function processed a request.
[2024-03-27T19:04:41.904Z] Returning Update Blob: 729ee33f-e283-420a-b898-648718139e3a to ip:
[2024-03-27T19:04:41.906Z] Exiting Get Update Blob: 729ee33f-e283-420a-b898-648718139e3a for ip:
[2024-03-27T19:04:41.907Z] Executed 'GetBlob' (Succeeded, Id=5b3af3ef-c61e-4ad7-a582-4982ba9d6b3e, Duration=7650ms)
[2024-03-27T19:04:41.915Z] An unhandled host error has occurred.
[2024-03-27T19:04:41.916Z] Microsoft.WindowsAzure.Storage: Object reference not set to an instance of an object.
请注意消息“执行‘GetBlob’(成功)”发生在我的函数执行之外/之后,随后是错误消息。退货未到达我正在测试的应用程序。
错误消息很模糊,我的调试器没有捕获发生此错误的步骤,但它肯定是在执行显示的其他所有内容之后发生的。我什至检查了 blob 是否已成功下载并附加到响应中。
我目前唯一的猜测是版本不匹配?以防万一,这也是我的项目文件:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.14">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.14" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.14">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="4.2.0" />
</ItemGroup>
<ItemGroup>
<None Update="host.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="local.settings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
</ItemGroup>
</Project>
更新:我尝试用 Azure.Storage.Blobs 替换 Microsoft.Windows.Azure:
private static BlobServiceClient GetStorageAccount()
{
var connParameter= "StorageAccountConnectionString";
var connectionString = System.Environment.GetEnvironmentVariable($"{connParameter}");
return new BlobServiceClient(connectionString);
}
private static Stream GetBlobStream(string id)
{
BlobServiceClient account = GetStorageAccount();
BlobContainerClient container = account.GetBlobContainerClient(GetContainerName());
var blob = container.GetBlobClient(id);
return blob.OpenReadAsync().Result;
}
现在,在一切似乎都成功执行之后,我在终端中收到了略有不同的错误:
[2024-03-28T12:09:20.144Z] Executed 'GetBlob' (Succeeded, Id=08567df1-e23f-4973-a9e5-a9750f0ff9ee, Duration=4304ms)
[2024-03-28T12:09:20.322Z] An unhandled host error has occurred.
[2024-03-28T12:09:20.323Z] System.Private.CoreLib: Value cannot be null. (Parameter 'buffer').
令人惊讶的是,本例中的罪魁祸首是 using 语句。
我将方法更改为同步运行并删除了 using 语句。我不确定为什么会这样,但该功能现在下载没有问题。我只能猜测这里发生了什么,但我认为这会强制 blob 下载进入阻塞操作,确保该对象可供响应对象使用。我很好奇是否有人知道为什么这有效。
if(blobInfo != null){
var blob = GetBlobClient(id);
var blobStream = blob.OpenReadAsync().Result;
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new StreamContent(blobStream);
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = blobInfo.BlobFriendlyName;
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
log.LogInformation(string.Format("Returning Update Blob: {0} to ip: {1}", id, clientIp));
return result;
}