问题描述
我正在尝试获取使用连接到进度条的.DownloadToStreamAsync()方法从Azure Blob存储下载文件的完整示例。
我找到了对Azure存储SDK旧实现的引用,但它们不能使用较新的SDK(已实现这些异步方法)编译,也不能使用当前的Nuget包。
https://blogs.msdn.microsoft.com/avkashchauhan/2010/11/03/uploading-a-blob-to-azure-storage-with-progress-bar-and-variable-upload-block-size/
https://blogs.msdn.microsoft.com/kwill/2013/03/05/asynchronous-parallel-blob-transfers-with-progress-change-notification-2-0/
我是.NET中异步/等待线程的新手,我想知道是否有人能帮我解决以下问题(在Windows窗体应用程序中),并向我展示如何"挂钩"到文件下载的进度中去。我看到一些示例不使用.DownloadToStream方法,而是下载BLOB文件的块。但是我想知道既然这些新的.async()方法已经存在于较新的Storage SDK中,是否可以做一些更聪明的事情呢?
假设下面正在运行(非异步),我还需要做些什么才能使用blockBlob.DownloadToStreamAsync(fileStream);方法,这是正确的用法吗?我如何才能获得进度?
理想情况下,无论如何我都可以挂接BLOB下载的进度,这样我就可以在大的下载中更新Windows窗体UI。所以如果下面不是正确的方法,请告诉我:)// Retrieve storage account from connection string.
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(
CloudConfigurationManager.GetSetting("StorageConnectionString"));
// Create the blob client.
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
// Retrieve reference to a previously created container.
CloudBlobContainer container = blobClient.GetContainerReference("mycontainer");
// Retrieve reference to a blob named "photo1.jpg".
CloudBlockBlob blockBlob = container.GetBlockBlobReference("photo1.jpg");
// Save blob contents to a file.
using (var fileStream = System.IO.File.OpenWrite(@"pathmyfile"))
{
blockBlob.DownloadToStream(fileStream);
}
使用Gaurav友好建议的method(下载1MB块),我已经实现了使用后台工作人员进行下载,这样我就可以在进行过程中更新UI。
do循环中的主要部分,它将范围下载到流,然后将流写入我在原始示例中没有触及的文件系统,但是我已经添加了更新工作进程和侦听工作取消(中止下载)的代码。不确定这是否可能是问题所在?为完整起见,以下是Worker_DoWork方法中的所有内容:
public void worker_DoWork(object sender, DoWorkEventArgs e)
{
object[] parameters = e.Argument as object[];
string localFile = (string)parameters[0];
string blobName = (string)parameters[1];
string blobContainerName = (string)parameters[2];
CloudBlobClient client = (CloudBlobClient)parameters[3];
try
{
int segmentSize = 1 * 1024 * 1024; //1 MB chunk
var blobContainer = client.GetContainerReference(blobContainerName);
var blob = blobContainer.GetBlockBlobReference(blobName);
blob.FetchAttributes();
blobLengthRemaining = blob.Properties.Length;
blobLength = blob.Properties.Length;
long startPosition = 0;
do
{
long blockSize = Math.Min(segmentSize, blobLengthRemaining);
byte[] blobContents = new byte[blockSize];
using (MemoryStream ms = new MemoryStream())
{
blob.DownloadRangeToStream(ms, startPosition, blockSize);
ms.Position = 0;
ms.Read(blobContents, 0, blobContents.Length);
using (FileStream fs = new FileStream(localFile, FileMode.OpenOrCreate))
{
fs.Position = startPosition;
fs.Write(blobContents, 0, blobContents.Length);
}
}
startPosition += blockSize;
blobLengthRemaining -= blockSize;
if (blobLength > 0)
{
decimal totalSize = Convert.ToDecimal(blobLength);
decimal downloaded = totalSize - Convert.ToDecimal(blobLengthRemaining);
decimal blobPercent = (downloaded / totalSize) * 100;
worker.ReportProgress(Convert.ToInt32(blobPercent));
}
if (worker.CancellationPending)
{
e.Cancel = true;
blobDownloadCancelled = true;
return;
}
}
while (blobLengthRemaining > 0);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
这是有效的,但是对于较大的文件(例如30MB),我有时会收到"无法在另一个进程中打开时写入文件错误."这个过程失败了。
推荐答案
使用您的代码:
using (var fileStream = System.IO.File.OpenWrite(@"pathmyfile"))
{
blockBlob.DownloadToStream(fileStream);
}
不可能显示进度,因为代码只有在下载完成时才会从该函数中发出。DownloadToStream
函数将在内部将大Blob拆分为区块并下载这些区块。
您需要做的是使用您的代码下载这些块。您需要做的是使用DownloadRangeToStream
方法。前段时间我回答了一个类似的问题,您可能会觉得有用:Azure download blob part。
这篇关于带进度条的Azure Blob存储异步下载的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!