如何在WebClient.DownloadFileAsync上实现超时

本文关键字:实现 超时 DownloadFileAsync WebClient | 更新日期: 2023-09-27 17:53:06

所以我认为Webclient.DownloadFileAysnc会有一个默认超时,但环顾文档,我找不到任何关于它的东西,所以我猜它没有。

我想从网上下载一个文件,像这样:

 using (WebClient wc = new WebClient())
 {
      wc.DownloadProgressChanged += ((sender, args) =>
      {
            IndividualProgress = args.ProgressPercentage;
       });
       wc.DownloadFileCompleted += ((sender, args) =>
       {
             if (args.Error == null)
             {
                  if (!args.Cancelled)
                  {
                       File.Move(filePath, Path.ChangeExtension(filePath, ".jpg"));
                  }
                  mr.Set();
              }
              else
              {
                    ex = args.Error;
                    mr.Set();
              }
        });
        wc.DownloadFileAsync(new Uri("MyInternetFile", filePath);
        mr.WaitOne();
        if (ex != null)
        {
             throw ex;
        }
  }

但是如果我关闭我的WiFi(模拟互联网连接下降),我的应用程序只是暂停和下载停止,但它永远不会通过DownloadFileCompleted方法报告。

出于这个原因,我想在我的WebClient.DownloadFileAsync方法上实现超时。这可能吗?

作为题外话,我正在使用。net 4,不想添加对第三方库的引用,所以不能使用Async/Await关键字

如何在WebClient.DownloadFileAsync上实现超时

您可以使用WebClient.DownloadFileAsync()。现在在计时器内部,你可以像这样调用CancelAsync():

System.Timers.Timer aTimer = new System.Timers.Timer();
System.Timers.ElapsedEventHandler handler = null;
handler = ((sender, args)
      =>
     {
         aTimer.Elapsed -= handler;
         wc.CancelAsync();
      });
aTimer.Elapsed += handler;
aTimer.Interval = 100000;
aTimer.Enabled = true;

创建自己的客户端

  public class NewWebClient : WebClient
    {
        protected override WebRequest GetWebRequest(Uri address)
        {
            var req = base.GetWebRequest(address);
            req.Timeout = 18000;
            return req;
        }
    } 

创建一个WebClientAsync类,在构造函数中实现定时器。这样你就不用把计时器代码复制粘贴到每一个实现中。

public class WebClientAsync : WebClient
{
    private int _timeoutMilliseconds;
    public EdmapWebClientAsync(int timeoutSeconds)
    {
        _timeoutMilliseconds = timeoutSeconds * 1000;
        Timer timer = new Timer(_timeoutMilliseconds);
        ElapsedEventHandler handler = null;
        handler = ((sender, args) =>
        {
            timer.Elapsed -= handler;
            this.CancelAsync();
        });
        timer.Elapsed += handler;
        timer.Enabled = true;
    }
    protected override WebRequest GetWebRequest(Uri address)
    {
        WebRequest request = base.GetWebRequest(address);
        request.Timeout = _timeoutMilliseconds;
        ((HttpWebRequest)request).ReadWriteTimeout = _timeoutMilliseconds;
        return request;
    }

    protected override voidOnDownloadProgressChanged(DownloadProgressChangedEventArgs e)
    {
        base.OnDownloadProgressChanged(e);
        timer.Reset(); //If this does not work try below
        timer.Start();
    }
}

此选项允许您在下载文件时失去网络连接时超时。

这里是另一个实现,我试图避免任何共享类/对象变量,以避免多次调用的麻烦:

 public Task<string> DownloadFile(Uri url)
        {
            var tcs = new TaskCompletionSource<string>();
            Task.Run(async () =>
            {
                bool hasProgresChanged = false;
                var timer = new Timer(new TimeSpan(0, 0, 20).TotalMilliseconds);
                var client = new WebClient();
                void downloadHandler(object s, DownloadProgressChangedEventArgs e) => hasProgresChanged = true;
                void timerHandler(object s, ElapsedEventArgs e)
                {
                    timer.Stop();
                    if (hasProgresChanged)
                    {
                        timer.Start();
                        hasProgresChanged = false;
                    }
                    else
                    {
                        CleanResources();
                        tcs.TrySetException(new TimeoutException("Download timedout"));
                    }
                }
                void CleanResources()
                {
                    client.DownloadProgressChanged -= downloadHandler;
                    client.Dispose();
                    timer.Elapsed -= timerHandler;
                    timer.Dispose();
                }
                string filePath = Path.Combine(Path.GetTempPath(), Path.GetFileName(url.ToString()));
                try
                {
                    client.DownloadProgressChanged += downloadHandler;
                    timer.Elapsed += timerHandler;
                    timer.Start();
                    await client.DownloadFileTaskAsync(url, filePath);
                }
                catch (Exception e)
                {
                    tcs.TrySetException(e);
                }
                finally
                {
                    CleanResources();
                }
                return tcs.TrySetResult(filePath);
            });
            return tcs.Task;
        }