带进度条的多个异步下载

Sho*_*cky 0 .net c# asynchronous webclient

我正在尝试从我的 Web 服务器下载文件夹,并且我想在进度条上显示下载了多少数据/总共下载了多少数据的进度。首先,我尝试使用WebClient.DownloadFile. 哪个工作完美,但没有触发DownloadProgressChangedEventHandler。我猜它只能通过异步下载激活。所以我已经将我的方法改写为WebClient.DownloadFileAsync. 这就是它变得复杂的地方。

例如,我的 Web 服务器上有 30 个文件,大小为 53 MB。我想下载所有 30 个文件并在进度条上显示下载进度(并在它下面显示带有 xx/53 MB 下载的标签)。

//Inicialized by opening dialog
private void DownloadForm_Shown(object sender, EventArgs e) {
   WebClient client = new WebClient();
   client.DownloadProgressChanged += client_DownloadProgressChanged;
   client.DownloadFileCompleted += client_DownloadFileCompleted;
   startDownload();
}

void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) {
    progressBar.Value = e.ProgressPercentage;
    labelProgress.Text = String.Format("Downloaded {0} of {1} bytes", e.BytesReceived, e.TotalBytesToReceive);
}

private void startDownload() {
   //files contains all URL links
   foreach (string str in files) {
      string urlDownload = HttpUtility.UrlPathEncode(str);
      //location is variable where file will be stored
      client.DownloadFileAsync(new Uri(urlDownload), location);

      //without this, async download will go crazy and wont download anything
      while (client.IsBusy) { }
   }
}
Run Code Online (Sandbox Code Playgroud)

我有这个代码,发生的事情是它将开始下载,但不会更新进度条,也不会更新标签。然后下载后它会在大约 0.5 秒内更新进度和标签,就是这样。我是这类事情的初学者,你能帮我找出错误吗?我知道如何为一个文件制作进度条。但是我必须做什么,才能为多个文件制作它?

编辑:可以在此处找到包含一些解决方案的完整代码:http : //pastebin.com/Hu4CCY8M 但调用downloadURLs()方法后 UI 将冻结。完成此方法后它将再次开始工作。

Pet*_*iho 6

如果没有一个可靠地重现问题的良好的最小、完整和可验证的代码示例,就不可能完全解决问题。也就是说,从您发布的代码中可以清楚地看出,此处的主要错误是您阻止了 UI 线程,从而防止在下载过程中对进度条的任何更改反映在用户界面上。

“发疯”不是一个精确的问题描述,甚至不是一个准确的描述。也就是说,我希望尝试同时开始所有的下载会导致WebClient对象抛出一个类似于“WebClient 不支持并发 I/O 操作”的异常。如果您希望(也许您应该希望)同时下载文件,您将需要有多个WebClient对象,每个并发下载一个。

鉴于您一次下载一个文件的明显意图,您需要在不阻塞 UI 线程的情况下执行此操作。从您的原始代码,您张贴不可能甚至完全相同的副本中的代码/粘贴,因为你使用的标识符clientstartDownload()方法没有它什么地方做声明,没有它可能是一样的局部变量clientDownloadForm_Shown()方法。因此,暂时忽略这种差异,以下是您发布的代码的变体,名义上可以解决您的问题:

private TaskCompletionSource<bool> _tcs;

private async void DownloadForm_Shown(object sender, EventArgs e) {
   WebClient client = new WebClient();
   client.DownloadProgressChanged += client_DownloadProgressChanged;
   client.DownloadFileCompleted += client_DownloadFileCompleted;
   await startDownload(client);
}

void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) {
    progressBar.Value = e.ProgressPercentage;
    labelProgress.Text = String.Format("Downloaded {0} of {1} bytes", e.BytesReceived, e.TotalBytesToReceive);
}

void client_DownloadFileCompleted(object sender, DownloadFileCompletedEventArgs e) {
    // whatever else you have in this event handler, and then...
    _tcs.SetResult(true);
}

private async Task startDownload(WebClient client) {
   //files contains all URL links
   foreach (string str in files) {
      string urlDownload = HttpUtility.UrlPathEncode(str);
      //location is variable where file will be stored
      _tcs = new TaskCompletionSource<bool>();
      client.DownloadFileAsync(new Uri(urlDownload), location);
      await _tcs.Task;
   }
   _tcs = null; 
}
Run Code Online (Sandbox Code Playgroud)

这样,该DownloadForm_Shown()方法将在下载过程中将控制权交还给 UI 线程。startDownload()每个下载的文件完成后,执行将在方法中恢复,以便可以开始下一个。

如果以上内容不能让您重回正轨,请改进问题,使其包含良好的 MCVE 和更精确的问题描述。