C # скачать асинхронно со списком очередей
-
28-10-2019 - |
Вопрос
Если вы не утруждаетесь чтением всего текста, вы можете перейти к двум последним точкам: p
Этот сайт уже много раз помогал мне в прошлом, но теперь мне действительно нужна помощь.
Проблема заключается в следующем:
-
Сначала я использовал функцию DownloadFile с опцией show 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 в основную форму ?
-
Как бы вы это решили? Вы бы также использовали backgroundworker, который просматривает массив файлов, или есть более простой и эффективный способ. Может быть, не с использованием DownloadFileAsync, а с ручной загрузкой сокета?
-
В основном я хочу загружать файлы синхронно, но иметь собственный пользовательский интерфейс (поэтому мне нужно использовать функции асинхронной загрузки). Ха-ха
Надеюсь, я достаточно информировал вас. Заранее спасибо.
Решение
Используйте модальное диалоговое окно, возможно, добавив индикатор выполнения для визуального удовлетворения, информирующий пользователя о том, что процесс работает.
Таким образом, вы можете выполнять асинхронную загрузку, не позволяя взаимодействовать с элементами управления основной формы.