从不将异步上传到 Azure Blob 存储 returns
Uploading async to Azure Blob Storage never returns
我正在尝试使用异步方法将文件上传到 Azure blob 存储,然后设置其元数据,但 UploadFromByteArrayAsync
方法从未 returns。
我有以下代码:
var connAzureBlob = ConfigurationManager.AppSettings["AzureBlobStorage"];
var storageAccount = CloudStorageAccount.Parse(connAzureBlob);
var blobClient = storageAccount.CreateCloudBlobClient();
var fileContainer = blobClient.GetContainerReference(ConfigurationManager.AppSettings["AzureBlobContainer"]);
if (!fileContainer.Exists())
{
await fileContainer.CreateAsync();
await fileContainer.SetPermissionsAsync(new BlobContainerPermissions {
PublicAccess = BlobContainerPublicAccessType.Blob
});
}
try
{
var fileBlob = fileContainer.GetBlockBlobReference(documentId.ToString());
await fileBlob.UploadFromByteArrayAsync(buffer, 0, buffer.Length);
Log.Info($"{nameof(SaveToBlobAsync)}: blob {documentId} uploaded.");
await fileBlob.FetchAttributesAsync();
fileBlob.Properties.ContentType = contentType;
fileBlob.Metadata["..."] = "...";
await fileBlob.SetMetadataAsync();
Log.Info($"{nameof(SaveToBlobAsync)}: {documentId} - metadata saved.");
}
catch (Exception exception)
{
Log.Error($"{nameof(SaveToBlobAsync)}: An exception was thrown while saving a file to a blob: ", exception);
throw;
}
使用上面的代码,我希望看到记录了以下消息:
SaveToBlobAsync: blob 123 uploaded.
SaveToBlobAsync: 123 - metadata saved.
但这些从未出现过。
但是,blob 似乎确实已存储(我可以使用 Azure Storage Explorer 查看其内容)但进程似乎没有移动到下一行(这将是第一条日志消息) .
如果我将所有异步调用切换为非异步调用,那么代码将按预期工作。
谁能解释为什么 UploadFromByteArrayAsync
似乎 return 没有?
更新:
我想我会添加更多关于上下文的信息,以防有帮助。
正在从 Web API 方法调用此代码。有问题的控制器没有异步代码,除了调用存储库方法外几乎没有做任何其他事情。存储库方法更新 SQL 数据库 - 同样,所有这些都是非异步代码 - 在调用与 blob 存储接口的 class 之前。
这个面向存储的 blob class 具有非异步方法,除了使用 .Wait()
或 .Result
.
调用它们的异步等价物外,它们几乎没有其他作用。
在这个案例中,第二阶段的存储库正在调用非异步版本,所以上面的代码实际上是用 .Wait()
.
调用的
在上述代码之前,从进入 Web API 的控制器开始,此 HTTP 请求中没有其他异步调用。
错误几乎可以肯定是在您的调用堆栈的更上方,其中某些方法阻塞了返回的任务(例如,Task<T>.Result
、Task.Wait
等)。如果要完成任务需要被阻塞的线程,这将死锁。
I explain this situation in full on my blog,但它的要点是这样的:
await
默认情况下将捕获 "context" 并使用它来恢复 async
方法。
- 如果此代码在 ASP.NET 请求上下文中执行,则此 "context" 是 ASP.NET
SynchronizationContext
;如果此代码在 UI 线程中执行,则此 "context" 是 UI SynchronizationContext
.
- ASP.NET
SynchronizationContext
和 UI SynchronizationContext
一次只允许一个线程。
因此,当返回的 Task
上的调用代码 阻塞 时,它 持有 该上下文中的一个线程,阻止 Task
完成。
我正在尝试使用异步方法将文件上传到 Azure blob 存储,然后设置其元数据,但 UploadFromByteArrayAsync
方法从未 returns。
我有以下代码:
var connAzureBlob = ConfigurationManager.AppSettings["AzureBlobStorage"];
var storageAccount = CloudStorageAccount.Parse(connAzureBlob);
var blobClient = storageAccount.CreateCloudBlobClient();
var fileContainer = blobClient.GetContainerReference(ConfigurationManager.AppSettings["AzureBlobContainer"]);
if (!fileContainer.Exists())
{
await fileContainer.CreateAsync();
await fileContainer.SetPermissionsAsync(new BlobContainerPermissions {
PublicAccess = BlobContainerPublicAccessType.Blob
});
}
try
{
var fileBlob = fileContainer.GetBlockBlobReference(documentId.ToString());
await fileBlob.UploadFromByteArrayAsync(buffer, 0, buffer.Length);
Log.Info($"{nameof(SaveToBlobAsync)}: blob {documentId} uploaded.");
await fileBlob.FetchAttributesAsync();
fileBlob.Properties.ContentType = contentType;
fileBlob.Metadata["..."] = "...";
await fileBlob.SetMetadataAsync();
Log.Info($"{nameof(SaveToBlobAsync)}: {documentId} - metadata saved.");
}
catch (Exception exception)
{
Log.Error($"{nameof(SaveToBlobAsync)}: An exception was thrown while saving a file to a blob: ", exception);
throw;
}
使用上面的代码,我希望看到记录了以下消息:
SaveToBlobAsync: blob 123 uploaded.
SaveToBlobAsync: 123 - metadata saved.
但这些从未出现过。
但是,blob 似乎确实已存储(我可以使用 Azure Storage Explorer 查看其内容)但进程似乎没有移动到下一行(这将是第一条日志消息) .
如果我将所有异步调用切换为非异步调用,那么代码将按预期工作。
谁能解释为什么 UploadFromByteArrayAsync
似乎 return 没有?
更新: 我想我会添加更多关于上下文的信息,以防有帮助。
正在从 Web API 方法调用此代码。有问题的控制器没有异步代码,除了调用存储库方法外几乎没有做任何其他事情。存储库方法更新 SQL 数据库 - 同样,所有这些都是非异步代码 - 在调用与 blob 存储接口的 class 之前。
这个面向存储的 blob class 具有非异步方法,除了使用 .Wait()
或 .Result
.
在这个案例中,第二阶段的存储库正在调用非异步版本,所以上面的代码实际上是用 .Wait()
.
在上述代码之前,从进入 Web API 的控制器开始,此 HTTP 请求中没有其他异步调用。
错误几乎可以肯定是在您的调用堆栈的更上方,其中某些方法阻塞了返回的任务(例如,Task<T>.Result
、Task.Wait
等)。如果要完成任务需要被阻塞的线程,这将死锁。
I explain this situation in full on my blog,但它的要点是这样的:
await
默认情况下将捕获 "context" 并使用它来恢复async
方法。- 如果此代码在 ASP.NET 请求上下文中执行,则此 "context" 是 ASP.NET
SynchronizationContext
;如果此代码在 UI 线程中执行,则此 "context" 是 UISynchronizationContext
. - ASP.NET
SynchronizationContext
和 UISynchronizationContext
一次只允许一个线程。
因此,当返回的 Task
上的调用代码 阻塞 时,它 持有 该上下文中的一个线程,阻止 Task
完成。