C#はキューリストと非同期にダウンロードします
-
28-10-2019 - |
質問
テキスト全体をわざわざ読まない場合は、最後の2つのドットにスキップできます:p
このサイトは過去に何十回も助けてくれましたが、今は本当に自分で助けが必要です。
問題は次のとおりです。
-
最初に、showUIオプションを指定してDownloadFile関数を使用しました。これはうまく機能しましたが、UIは見苦しく、利用できるオプションは多くありません。
-
次に、基本的に独自のUIを使用するために、変更された進行状況イベントを使用してDownloadFileAsyncに切り替えました。私が抱えていた唯一の問題は、プログラムがダウンロードしなければならないファイルのリストをループして、ダウンロード関数(DownloadAsync関数を呼び出す)を呼び出すことです。このように:
foreach (ListViewItem t in themeList.CheckedItems) { DownloadFile(file to be downloaded); }
-
しかし、DownloadFileAsync関数は、DownloadFileのようなキューシステムがないため、同時に複数の呼び出しをサポートしないため、明らかにこれは機能しませんでした。したがって、最初に呼び出されたファイルのみがダウンロードされます。 だから私がしたのは、ダウンロードするファイルを配列に追加し、backgroundworkerがリストをループして、前回のダウンロードが完了するまで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があります(したがって、非同期ダウンロード機能を使用する必要があります)。ハハ
十分にお知らせしたいと思います。よろしくお願いします。
解決
モーダルダイアログを使用します。視覚的に満足できるプログレスバーを追加して、プロセスが機能していることをユーザーに通知します。
このようにして、メインフォームのコントロールとの対話を許可せずにダウンロード非同期を実行できます。