C#与队列列表异步下载
-
28-10-2019 - |
题
如果您不想阅读全文,则可以跳到最后两个点:p
该站点过去已经为我提供了十多次帮助,但是现在我真的需要自己帮助。
问题如下:
-
首先,我将DownloadFile函数与show 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,而是手动进行套接字下载?
-
我基本上想要的是同步下载文件,但是有我自己的UI(因此我需要使用异步下载功能)。哈哈
希望我能告诉您足够的信息。预先感谢。
解决方案
使用“模态”对话框,可能会添加进度条以实现视觉上的满足,告知用户流程正在运行。
这样,您可以执行下载异步操作而无需与主窗体的控件进行交互。