使用.NET 4.5异步将Azure blob异步下载到字符串,等待

本文关键字:异步 下载 字符串 等待 blob Azure NET 使用 | 更新日期: 2023-09-27 18:27:37

我正试图用.NET 4.5异步&等候

让我们假设整个blob可以同时放入内存,并且我们希望将其保存在string中。

代码:

public async Task<string> DownloadTextAsync(ICloudBlob blob)
{
    using (Stream memoryStream = new MemoryStream())
    {
        IAsyncResult asyncResult = blob.BeginDownloadToStream(memoryStream, null, null);
        await Task.Factory.FromAsync(asyncResult, (r) => { blob.EndDownloadToStream(r); });
        memoryStream.Position = 0;
        using (StreamReader streamReader = new StreamReader(memoryStream))
        {
            // is this good enough?
            return streamReader.ReadToEnd();
            // or do we need this?
            return await streamReader.ReadToEndAsync();
        }
    }
}

用法:

CloudStorageAccount storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageAccountConnectionString"));
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer container = blobClient.GetContainerReference("container1");
CloudBlockBlob blockBlob = container.GetBlockBlobReference("blob1.txt");
string text = await DownloadTextAsync(blockBlob);

这段代码是否正确,并且确实是完全异步的?你会以不同的方式实现这一点吗?

我非常感谢您的进一步澄清:

  1. GetContainerReferenceGetBlockBlobReference不需要异步,因为它们还没有联系服务器,对吧?

  2. streamReader.ReadToEnd是否需要异步?

  3. 我对BeginDownloadToStream的作用有点困惑。。调用EndDownloadToStream时,我的内存流中是否包含所有数据?还是流只在预读时打开?

更新:(自存储2.1.0.0 RC起)

现在本机支持异步。

CloudStorageAccount storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageAccountConnectionString"));
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer container = blobClient.GetContainerReference("container1");
CloudBlockBlob blockBlob = container.GetBlockBlobReference("blob1.txt");
string text = await blockBlob.DownloadTextAsync();

使用.NET 4.5异步将Azure blob异步下载到字符串,等待

这段代码是否正确,并且确实是完全异步的?

是的。

你会以不同的方式实现这一点吗?

是的。特别是,如果传入Begin/End方法对,而不是传入现有的IAsyncResult,那么TaskFactory.FromAsync包装器的效率会高得多。像这样:

await Task.Factory.FromAsync(blob.BeginDownloadToStream,
    blob.EndDownloadToStream, memoryStream, null);

我也更喜欢把这些封装成单独的扩展方法,这样我就可以这样称呼它:

await blog.DownloadToStreamAsync(memoryStream);

请注意,客户端库的下一个版本(2.1,目前在RC中)将具有async就绪方法,即DownloadToStreamAsync

GetContainerReference和GetBlockBlobReference不需要异步,因为它们还没有联系服务器,对吧?

正确。

streamReader.ReadToEnd是否需要异步?

它没有(也不应该)。Streamasync编程中有点不寻常。通常,如果有async方法,那么您应该在async代码中使用它,但该准则不适用于Stream类型。原因是Stream基类不知道它的实现是同步的还是异步的,所以它假设它是同步的,默认情况下,它只会通过在后台线程上执行同步工作来伪造异步操作。真正的异步流(例如NetworkStream)覆盖了这一点,并提供了真正的异步操作。同步流(例如MemoryStream)保持这种默认行为。

所以您不想在MemoryStream上调用ReadToEndAsync

我对BeginDownloadToStream的作用有点困惑。。当调用EndDownloadToStream时,我的内存流中是否包含所有数据?

是的。操作为DownloadToStream;它将blob下载到流中。由于您正在将blob下载到MemoryStream中,因此此操作完成时blob已完全在内存中。

  1. 正确,如果它们不是长操作,就不需要异步,这是不应该的。

  2. 可能不会,尽管我不熟悉这个特定的实现。我希望,由于您正在等待流在此点之前结束,因此此时不应该执行任何网络工作,也不应该执行昂贵的操作。您应该只是从缓冲区中提取数据,而且应该很快。然而,这很容易测试。您可以使用Fiddler之类的工具来查看在该调用期间是否有网络通信正在进行,您可以对该方法计时,以查看是否需要足够长的时间才能显示网络IO正在进行,或者您可以查看该特定流实现的文档。或者,为了安全起见,我建议您使用async方法,而不是冒着出错的风险。如果我发现这个需要是异步的,我会非常惊讶。

  3. 参见#2。

请参阅:http://channel9.msdn.com/Events/TechEd/NorthAmerica/2013/WAD-B406#fbid=lCN9J5QiTDF对于一些有用的最佳实践,包括为什么应该避免像原始代码那样使用内存流:)

需要注意的是,下载Blob有两个主要选项,Cloud[Block|Page]Blob。Download[Range]To*方法和OpenRead()提供的流。在下载api的情况下,整个blob(或范围,如果请求)作为单个GET调用发出,并且结果被流式传输/写入到适当的位置,在瞬态故障的情况下根据重试策略请求尚未接收的字节的子范围。

OpenRead方法适用于那些希望在较长时间内处理数据而不保持连接打开的客户端。它们通过指定将在客户端预缓冲的给定长度来工作,当流用完预缓冲的数据时,请求下一个子范围。

最后,从2.1 RTM开始,提供了一个DownloadTextAsync方法,可以为您完成所有这些操作:)(带有指定编码的可选重载,默认为UTF8)