I'm writing an application that manages a library of mods/addons for a game. Every so often, one of these mods has an update available, and the new version is downloaded using WebClient.DownloadFileAsync(), having retrieved the filename through reading the Content-Disposition in a WebRequest response.
When two or more updates are available, the first downloads perfectly fine but when you try to download a second file without having restarted the application WebClient freezes, the file is created with the retrieved name, but contains 0 bytes and neither the DownloadProgressChanged or DownloadFileCompleted events are triggered.
If I do not try to retrieve the original filename, and just give them a name locally, then WebClient doesn't freeze. But I need the original filename.
Is there anything I can do to avoid this issue, while still being able to retrieve the original filename?
private void Download(string url, string downloadDirectory)
{
WebClient wc = new WebClient();
string filename = FilenameFromURL(url);
wc.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadProgress);
wc.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadComplete);
wc.DownloadFileAsync(new Uri(url), downloadDirectory + filename);
}
private string FilenameFromURL(string url)
{
return new ContentDisposition(WebRequest.Create(url).GetResponse().Headers["Content-Disposition"].ToString()).FileName;
}
private void DownloadProgress(object sender, DownloadProgressChangedEventArgs e)
{
downloadProgressBar.Value = e.ProgressPercentage;
}
private void DownloadComplete(object sender, AsyncCompletedEventArgs e)
{
// ...
}
I managed to fix the problem by passing the instance of WebClient that was downloading the file through the EventHandler's parameters and then using that to get the original filename, rather than using a seperate instance of it to retrieve the filename.
Credit to this question for helping me figure out the solution.
With this solution, because I can't find out the original filename until after the file has been downloaded, I assign it a temporary name for the download, and rename it when the download is complete in the AsyncCompletedEventHandler.
private void Download(string url, string downloadDirectory)
{
WebClient wc = new WebClient();
wc.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadProgress);
wc.DownloadFileCompleted += new AsyncCompletedEventHandler((sender, e) => DownloadComplete(sender, e, wc));
wc.DownloadFileAsync(new Uri(url), downloadDirectory + "temp");
}
private void DownloadProgress(object sender, DownloadProgressChangedEventArgs e)
{
downloadProgressBar.Value = e.ProgressPercentage;
}
private void DownloadComplete(object sender, AsyncCompletedEventArgs e, WebClient wc)
{
string filename = new ContentDisposition(wc.ResponseHeaders["Content-Disposition"].ToString()).FileName;
if (File.Exists(downloadDirectory + "temp"))
{
if (File.Exists(downloadDirectory + filename))
File.Delete(downloadDirectory + filename);
File.Move(downloadDirectory + "temp", downloadDirectory + filename);
}
// ...
}