Como faço para baixar vários arquivos usando o web client, mas um de cada vez?

Tem sido surpreendentemente difícil encontrar um exemplo de código para baixar vários arquivos usando o método asynchronous da class webclient, mas baixando um de cada vez.

Como posso iniciar um download asynchronous, mas aguarde até que o primeiro termine até o segundo, etc. Basicamente um que.

(note que não quero usar o método sync, devido à maior funcionalidade do método async).

O código abaixo inicia todos os meus downloads de uma só vez. (a barra de progresso está em todo o lugar)

private void downloadFile(string url) { WebClient client = new WebClient(); client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged); client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted); // Starts the download btnGetDownload.Text = "Downloading..."; btnGetDownload.Enabled = false; progressBar1.Visible = true; lblFileName.Text = url; lblFileName.Visible = true; string FileName = url.Substring(url.LastIndexOf("/") + 1, (url.Length - url.LastIndexOf("/") - 1)); client.DownloadFileAsync(new Uri(url), "C:\\Test4\\" + FileName); } void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) { } void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) { double bytesIn = double.Parse(e.BytesReceived.ToString()); double totalBytes = double.Parse(e.TotalBytesToReceive.ToString()); double percentage = bytesIn / totalBytes * 100; progressBar1.Value = int.Parse(Math.Truncate(percentage).ToString()); } 

O que eu fiz foi preencher uma fila contendo todos os meus urls, então eu baixei cada item na fila. Quando não há itens restantes, posso processar todos os itens. Eu zombei de algum código abaixo. Tenha em mente que o código abaixo é para o download de strings e não de arquivos. Não deve ser muito difícil modificar o código abaixo.

  private Queue _items = new Queue(); private List _results = new List(); private void PopulateItemsQueue() { _items.Enqueue("some_url_here"); _items.Enqueue("perhaps_another_here"); _items.Enqueue("and_a_third_item_as_well"); DownloadItem(); } private void DownloadItem() { if (_items.Any()) { var nextItem = _items.Dequeue(); var webClient = new WebClient(); webClient.DownloadStringCompleted += OnGetDownloadedStringCompleted; webClient.DownloadStringAsync(new Uri(nextItem)); return; } ProcessResults(_results); } private void OnGetDownloadedStringCompleted(object sender, DownloadStringCompletedEventArgs e) { if (e.Error == null && !string.IsNullOrEmpty(e.Result)) { // do something with e.Result string. _results.Add(e.Result); } DownloadItem(); } 

Edit: Eu modifiquei o seu código para usar uma fila. Não totalmente certo de como você queria que o progresso funcionasse. Tenho certeza de que se você quiser que o progresso atenda a todos os downloads, poderá armazenar a contagem de itens no método ‘PopulateItemsQueue ()’ e usar esse campo no método de progresso alterado.

  private Queue _downloadUrls = new Queue(); private void downloadFile(IEnumerable urls) { foreach (var url in urls) { _downloadUrls.Enqueue(url); } // Starts the download btnGetDownload.Text = "Downloading..."; btnGetDownload.Enabled = false; progressBar1.Visible = true; lblFileName.Visible = true; DownloadFile(); } private void DownloadFile() { if (_downloadUrls.Any()) { WebClient client = new WebClient(); client.DownloadProgressChanged += client_DownloadProgressChanged; client.DownloadFileCompleted += client_DownloadFileCompleted; var url = _downloadUrls.Dequeue(); string FileName = url.Substring(url.LastIndexOf("/") + 1, (url.Length - url.LastIndexOf("/") - 1)); client.DownloadFileAsync(new Uri(url), "C:\\Test4\\" + FileName); lblFileName.Text = url; return; } // End of the download btnGetDownload.Text = "Download Complete"; } private void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) { if (e.Error != null) { // handle error scenario throw e.Error; } if (e.Cancelled) { // handle cancelled scenario } DownloadFile(); } void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) { double bytesIn = double.Parse(e.BytesReceived.ToString()); double totalBytes = double.Parse(e.TotalBytesToReceive.ToString()); double percentage = bytesIn / totalBytes * 100; progressBar1.Value = int.Parse(Math.Truncate(percentage).ToString()); } 

Eu estou lutando para entender onde está o problema. Se você só chamar o método asynchronous para o primeiro arquivo, ele não fará o download apenas desse arquivo? Por que não usar o evento client_downlaodFileCompleted para iniciar o próximo download de arquivo com base em algum valor passado por AsyncCompletedEvents, ou manter uma lista de arquivos baixados como uma variável estática e fazer com que client_DownloadFileCompleted repita a lista para encontrar o próximo arquivo a ser baixado.

Espero que ajude, mas por favor, poste mais informações se eu tiver entendido sua pergunta.

Gostaria de fazer um novo método, por exemplo, chamado getUrlFromQueue que me retorna um novo URL da fila (coleção ou matriz) e exclui-lo .. então ele chama downloadFile (url) – e em client_DownloadFileCompleted eu chamo getUrlFromQueue novamente.