问题
I'm trying to download folder from my web server and I want to display progress on progress bar with how many data was downloaded / total to download. First I've tried to use WebClient.DownloadFile
. Which worked flawlessly, but it didn't trigger DownloadProgressChangedEventHandler
. I guess it's activated only by async downloading. So I've rework my method to WebClient.DownloadFileAsync
. This is where it gets complicated.
For example, I have 30 files on my web server with size 53 MB. I want to download all 30 files and show on progress bar what the progress of downloading (and under it show label with xx/53 MB download).
//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) { }
}
}
I have this code and what's happening is that it will start downloading but it won't update progress bar, nor the label. And then after downloading it will update progress and label in about 0.5sec and that's it. I'm beginner in these kind of things, could you please help me find an error? I know how to make progress bar for ONE file. But what I have to do, to make it for multiple files?
EDIT: Full code with some solution can be found here: http://pastebin.com/Hu4CCY8M
But the UI will freeze after calling downloadURLs()
method. And it will start working again after finishing this method.
回答1:
Without a good Minimal, Complete, and Verifiable code example that reliably reproduces the problem, it's impossible to address the issue completely. That said, it's clear from the code you posted that the main thing wrong here is that you've blocked the UI thread, preventing any changes to the progress bar from being reflected on the user interface while the downloads are in progress.
"Go crazy" isn't a precise problem description, or even close to one. That said, I would expect that trying to start all of the downloads concurrently to cause the WebClient
object to throw an exception along the lines of "WebClient does not support concurrent I/O operations". If you want (and perhaps you should want) to download files concurrently, you'll need to have multiple WebClient
objects, one for each concurrent download.
Given your apparent intent to download the files one at a time, you need to do so without blocking the UI thread. The code you posted couldn't possibly be even an exact copy/paste from your original code, because you use the identifier client
in the startDownload()
method without it being declared anywhere, and without it possibly being the same as the local variable client
in the DownloadForm_Shown()
method. So, ignoring that discrepancy for the moment, here's a variation on the code you posted that will nominally address your issue:
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;
}
This way, the DownloadForm_Shown()
method will yield control back to the UI thread while a download is progressing. Execution will resume back in the startDownload()
method after each downloaded file has completed, so that the next can be started.
If the above does not get you back on track, please improve the question so that it includes a good MCVE and a more precise problem description.
来源:https://stackoverflow.com/questions/39552021/multiple-asynchronous-download-with-progress-bar