给定 Uris 列表时,WebClient 不会下载所有文件

WebClient doesn't download all files when given a list of Uris

我正在制作一个工具,用于同时使用 List<Uri>WebClient class 从 Internet 下载图像。这是相关代码:

我正在使用的新 WebClient:

public class PatientWebClient : WebClient
{
    protected override WebRequest GetWebRequest(Uri uri)
    {
        WebRequest w = base.GetWebRequest(uri);
        w.Timeout = Timeout.Infinite;
        return w;
    }
}

及下载方式:

    public static void DownloadFiles()
    {
        string filename = string.Empty;

        while (_count < _images.Count())
        {
            PatientWebClient client = new PatientWebClient();

            client.DownloadDataCompleted += DownloadCompleted;
            filename = _images[_count].Segments.Last().ToString();
            if (!File.Exists(_destinationFolder + @"\" + filename))
            {
                try
                {
                    client.DownloadDataAsync(_images[_count], _images[_count]);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                }
             }
            ++_count;
        }
    }

    private static void DownloadCompleted(object sender, DownloadDataCompletedEventArgs e)
    {
        if (e.Error == null)
        {
            Uri uri = (Uri)e.UserState;
            string saveFilename = uri.Segments.Last().ToString();

            byte[] fileData = e.Result;

            if (saveFilename.EndsWith(".jpg") || saveFilename.EndsWith(".png") || saveFilename.EndsWith(".gif"))
                using (FileStream fileStream = new FileStream(_destinationFolder + @"\" + saveFilename, FileMode.Create))
                    fileStream.Write(fileData, 0, fileData.Length);
            else
                using (FileStream fileStream = new FileStream(_destinationFolder + @"\" + saveFilename + ".jpg", FileMode.Create))
                    fileStream.Write(fileData, 0, fileData.Length);
            ++_downloadedCounter;
            ((WebClient)sender).Dispose();
        }
    }

问题是并非列表 _images 中的所有图像都在下载。如果我第二次点击下载按钮,将会下载更多,实际上需要点击几下才能下载所有内容。 WebClient 是否超时,如果超时,是否有办法让它们自动重试下载?如果不是,解决这个问题的正确方法是什么?

我的意思是这样的,设置 webclient 超时并捕获错误:

  internal class Program
  {
    private static void Main(string[] args)
    {
      Uri[] uris = {new Uri("http://www.google.com"), new Uri("http://www.yahoo.com")};
      Parallel.ForEach(uris, uri =>
      {
        using (var webClient = new MyWebClient())
        {
          try
          {
            var data = webClient.DownloadData(uri);
            // Success, do something with your data
          }
          catch (Exception ex)
          {
            // Something is wrong...
            Console.WriteLine(ex.ToString());
          }
        }
      });
    }
  }

  public class MyWebClient : WebClient
  {
    protected override WebRequest GetWebRequest(Uri uri)
    {
      var w = base.GetWebRequest(uri);
      w.Timeout = 5000; // 5 seconds timeout
      return w;
    }
  }

如果你仍然想使用那个模式,这个模式没有超时,你必须使用定时器来实现:

  internal class Program
  {
    private static int _downloadCounter;
    private static readonly object _syncObj = new object();

    private static void Main(string[] args)
    {
      Uri[] uris = {new Uri("http://www.google.com"), new Uri("http://www.yahoo.com")};
      foreach (var uri in uris)
      {
        var webClient = new WebClient();
        webClient.DownloadDataCompleted += OnWebClientDownloadDataCompleted;
        webClient.DownloadDataAsync(uri);
      }
      Thread.Sleep(Timeout.Infinite);
    }

    private static void OnWebClientDownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
    {
      if (e.Error == null)
      {
        // OK
        Console.WriteLine(Encoding.UTF8.GetString(e.Result));
      }
      else
      {
        // Error
        Console.WriteLine(e.Error.ToString());
      }

      lock (_syncObj)
      {
        _downloadCounter++;
        Console.WriteLine("Counter = {0}", _downloadCounter);
      }

      var webClient = sender as WebClient;
      if (webClient == null) return;
      webClient.DownloadDataCompleted -= OnWebClientDownloadDataCompleted;
      webClient.Dispose();
    }
  }