C# 如何在WebClient.DownloadFileAsync上实现超时

C# 如何在WebClient.DownloadFileAsync上实现超时,c#,.net-4.0,timeout,webclient,C#,.net 4.0,Timeout,Webclient,所以我想,Webclient.DownloadFileAysnc会有一个默认超时,但是查看文档时,我在任何地方都找不到关于它的任何信息,所以我猜不会 我正在尝试从internet下载一个文件,如下所示: using (WebClient wc = new WebClient()) { wc.DownloadProgressChanged += ((sender, args) => { IndividualProgress = args.P

所以我想,
Webclient.DownloadFileAysnc
会有一个默认超时,但是查看文档时,我在任何地方都找不到关于它的任何信息,所以我猜不会

我正在尝试从internet下载一个文件,如下所示:

 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()。现在,在计时器中,您可以调用CancelAsync(),如下所示:

否则创建您自己的weclient

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

这将允许您在下载文件时丢失Internet连接时超时。

这里是另一个实现,我尝试避免任何共享类/对象变量,以避免多次调用时出现问题:

 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;
        }
公共任务下载文件(Uri url) { var tcs=new TaskCompletionSource(); Task.Run(异步()=> { bool hasProgresChanged=false; var timer=新计时器(新时间跨度(0,0,20).total毫秒); var client=new WebClient(); void downloadHandler(对象s,DownloadProgressChangedEventArgs e)=>hasprogreChanged=true; 无效时间句柄(对象s、ElapsedEventArgs e) { timer.Stop(); 如果(hasProgresChanged) { timer.Start(); hasProgresChanged=false; } 其他的 { 清洁资源(); tcs.TrySetException(新的TimeoutException(“下载timedout”); } } void CleanResources() { client.DownloadProgressChanged-=downloadHandler; client.Dispose(); timer.appeased-=timerHandler; timer.Dispose(); } 字符串filePath=Path.Combine(Path.GetTempPath(),Path.GetFileName(url.ToString()); 尝试 { client.DownloadProgressChanged+=downloadHandler; timer.appeased+=timerHandler; timer.Start(); wait client.DownloadFileTaskAsync(url,文件路径); } 捕获(例外e) { tSystexception(e); } 最后 { 清洁资源(); } 返回tcs.TrySetResult(文件路径); }); 返回tcs.Task; }
非常糟糕的方法,计算时间需要额外的线程,而这完全是错误的unncessary@Kilanny你可以自由回答更好的方法。这就是stackoverflow的作用。通过共享学习新知识单独创建自己的webclient无法与async一起工作。您的第一个答案是,使用CancelAsync是我所知道的实现异步web请求超时的唯一方法。谢谢回答得好。下面我将发布一个结合了两者的答案。提供了一种我认为可以更好地完成任务的通用方法。
 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;
        }