2011-08-09 8 views
24

Ha sido sorprendentemente difícil encontrar un ejemplo de código de descarga de múltiples archivos usando el método asíncrono de la clase webcliente, pero descargando uno a la vez.¿Cómo descargo varios archivos usando webclient, pero de a uno por vez?

¿Cómo puedo iniciar una descarga asíncrona, pero esperar hasta que la primera termine hasta la segunda, etc. Básicamente una que.

(tenga en cuenta que no quiero utilizar el método de sincronización, debido a la mayor funcionalidad del método asíncrono.)

El código de abajo comienza todos mis descargas a la vez. (La barra de progreso es por todo el 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()); 
     } 
+0

Usted puede estar interesado en esta pregunta relacionada: [Llamar a un método asíncrono en serie] (http://stackoverflow.com/questions/3909063/calling-an-asynchronous-method-serially) – Benjol

Respuesta

34

Lo que he hecho es rellenar una cola que contiene todas mis direcciones URL, luego descargo cada elemento en la cola. Cuando no quedan elementos, puedo procesar todos los elementos. Me he burlado de algunos códigos a continuación. Tenga en cuenta que el código siguiente es para descargar cadenas y no archivos. No debería ser demasiado difícil modificar el código a continuación.

private Queue<string> _items = new Queue<string>(); 
    private List<string> _results = new List<string>(); 

    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(); 
    } 

Editar: he modificado el código para utilizar una cola. No del todo seguro de cómo quería que el progreso funcione. Estoy seguro de que si desea que el progreso cubra todas las descargas, entonces puede almacenar el recuento de elementos en el método 'PopulateItemsQueue()' y usar ese campo en el método de cambio de progreso.

private Queue<string> _downloadUrls = new Queue<string>(); 

    private void downloadFile(IEnumerable<string> 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()); 
    } 
+0

¿Qué pasaría si la descarga falla (o no se completa por alguna razón), entonces el resto de la cola no se descargaría ya que client_DownloadFileCompleted nunca se activaría ¿verdad? ¿Hay alguna manera de verificar esto, una función de tiempo de espera o algo así? – Dan

+0

@Dan: si se produce un error, se puede detectar en el método client_DownloadFileCompleted marcando la propiedad Error del parámetro AsyncCompletedEventArgs. Actualicé el código para demostrar dónde y cómo se podría hacer esto. –

+0

'WebClient' es desechable ¿no es así? – Shimmy

1

Me gustaría hacer un nuevo método, por ejemplo el nombre getUrlFromQueue que me devuelve una nueva URL de la cola (colección o matriz) y lo elimina .. A continuación, llama downloadFile (url) - y en client_DownloadFileCompleted Vuelvo a llamar a getUrlFromQueue.

2

Estoy luchando por entender dónde está el problema. Si solo llama al método asíncrono para el primer archivo, ¿no solo descargará ese archivo? ¿Por qué no utilizar el evento client_downlaodFileCompleted para iniciar la próxima descarga de archivos en función de algún valor aprobado por AsyncCompletedEvents, o mantener una lista de archivos descargados como una variable estática y tener client_DownloadFileCompleted iterate tht list para encontrar el siguiente archivo para descargar.

Espero que ayude, pero por favor publique más información si he entendido mal su pregunta.

Cuestiones relacionadas