【发布时间】:2011-11-14 09:23:38
【问题描述】:
如果你懒得看全文,你可以跳到最后两个点:p
这个网站过去已经帮助了我十几次,但现在我自己真的需要帮助。
问题如下:
首先我使用了带有显示 UI 选项的 DownloadFile 函数。这很好用,但 UI 很丑,可用的选项不多。
-
然后我切换到带有更改的进度事件的 DownloadFileAsync,以便基本上拥有自己的 UI。我遇到的唯一问题是我遍历程序必须下载的文件列表并调用下载函数(调用 DownloadAsync 函数)。像这样:
foreach (ListViewItem t in themeList.CheckedItems) { DownloadFile(file to be downloaded); } -
但显然这不起作用,因为DownloadFileAsync函数不支持同时调用多个调用,因为没有像DownloadFile那样的队列系统,所以它只会下载第一个调用的文件。 所以我所做的是创建一个函数,将要下载的文件添加到一个数组中,并在列表中进行一个后台工作循环,并等待调用 DownloadAsync,直到上一次下载完成。这有点奏效。代码如下:
#region "Download functions" //Function that converts download speed to a nice user friendly format private static string BpsToString(double bps) { var m = new string[] { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" }; var i = 0; while (bps >= 0.9 * 1024) { bps /= 1024; i++; } return String.Format("{0:0.00} {1}/sec", bps, m[i]); } private bool _complete = false; private string _speed; private int _secondsRemaining = -1; private long _transferred = 0; private Stopwatch _sw = new Stopwatch(); private List<string[]> _fd = new List<string[]>(); private void DownloadFile(string url, string des, bool overwrite = false) { if (overwrite) //if the file needs to be overwritten or not { if (File.Exists(des)) File.Delete(des); } else { if (File.Exists(des)) return; } if (!Directory.Exists(Path.GetDirectoryName(des))) //create the directory if it doesn't exist Directory.CreateDirectory(Path.GetDirectoryName(des)); string[] file = {url, des}; _fd.Add(file); //add file to queue list if(!backgroundDownloader.IsBusy) //if downloader isn't doing anything, start it again backgroundDownloader.RunWorkerAsync(); } //function called by the backgroundworker to actually download the file private void ContinueDownloadFile(string url, string des) { var webClient = new WebClient(); webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(Completed); webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(ProgressChanged); webClient.DownloadFileAsync(new Uri(_fd[0][0]), _fd[0][1]); } //when download completed, set progress bar to 0% and remove the first (0) download from the queue private void Completed(object sender, AsyncCompletedEventArgs e) { SetProgressText("Idle"); SetProgressValue(0); if(_fd.Count != 0) _fd.RemoveAt(0); _complete = true; //if it's complete, set to true so the backgroundworker knows it can start the next download } //progress bar value change and status change for download speed etc... private void ProgressChanged(object sender, DownloadProgressChangedEventArgs e) { if(progressLabel.Text == "Idle") SetProgressText("Downloading..."); if (_sw.Elapsed >= TimeSpan.FromSeconds(1)) { _sw.Stop(); var bytes = e.BytesReceived - _transferred; var bps = bytes * 1000.0 / _sw.Elapsed.TotalMilliseconds; _speed = BpsToString(bps); _secondsRemaining = (int)((e.TotalBytesToReceive - e.BytesReceived) / bps); _transferred = e.BytesReceived; _sw.Reset(); _sw.Start(); SetProgressText("Downloading: " + e.ProgressPercentage + "% | Seconds remaining: " + _secondsRemaining + " | Files remaining: " + _fd.Count + " | Speed: " + _speed); } SetProgressValue(e.ProgressPercentage); } //the backgroundworker who starts the downloads from the list one by one private void BackgroundDownloaderDoWork(object sender, DoWorkEventArgs e) { while (_fd.Count != 0) { _sw.Start(); _complete = false; //let the backgroundworker wait till the download is complete ContinueDownloadFile(_fd[0][0], _fd[0][1]); while(!_complete) //let it wait here Thread.Sleep(100); _sw.Stop(); _sw.Reset(); } } #endregion -
所以基本上我的下一个问题是程序必须等待执行更多代码,直到下载完成。我是这样做的:
while (_fd.Count != 0) Application.DoEvents(); 这显然不是最好的解决方案,因为他们可以在下载繁忙时单击其他内容,但是是的,Thread.Sleep 只会冻结所有内容。 相反,我会制作一个等待表单(也许这里是一个进度条,而不是主表单),将焦点放在主表单的顶部,因此他们无法单击主表单并将 Thread.Sleep 放在主表单上?
您将如何解决这个问题?您是否还会使用遍历文件数组的后台工作程序,或者是否有更简单、更有效的方法。可能不使用DownloadFileAsync,而是手动下载socket?
我基本上想要的是同步下载文件,但有自己的UI(所以我需要使用异步下载功能)。哈哈
我希望我已经通知了你足够多的信息。提前致谢。
【问题讨论】:
-
也许这是一篇“TL;DR”帖子?
-
我会让它更简短,因为它确实包含一些不必要的东西
-
您关于第 5 条的计划听起来是最好的方法。您也可以尝试使用任务并行库 (TPL)。它将简化并发下载调用,并且您可以使用同步文件下载器。
标签: c# downloadfile downloadfileasync