我试图下载一个客户端的数据到我的本地机器(编程),他们的web服务器非常非常慢,这导致我的WebClient对象超时。

这是我的代码:

WebClient webClient = new WebClient();

webClient.Encoding = Encoding.UTF8;
webClient.DownloadFile(downloadUrl, downloadFile);

有没有办法在这个对象上设置无限超时?如果不是,有没有人能帮我举个例子告诉我另一种方法?

该URL在浏览器中工作正常-只需要大约3分钟就能显示出来。


当前回答

对于任何需要WebClient超时工作的异步/任务方法的人,建议的解决方案将不起作用。以下是有效的方法:

public class WebClientWithTimeout : WebClient
{
    //10 secs default
    public int Timeout { get; set; } = 10000;

    //for sync requests
    protected override WebRequest GetWebRequest(Uri uri)
    {
        var w = base.GetWebRequest(uri);
        w.Timeout = Timeout; //10 seconds timeout
        return w;
    }

    //the above will not work for async requests :(
    //let's create a workaround by hiding the method
    //and creating our own version of DownloadStringTaskAsync
    public new async Task<string> DownloadStringTaskAsync(Uri address)
    {
        var t = base.DownloadStringTaskAsync(address);
        if(await Task.WhenAny(t, Task.Delay(Timeout)) != t) //time out!
        {
            CancelAsync();
        }
        return await t;
    }
}

我在博客上写了完整的解决方法

其他回答

不能让w.t otimeout代码在拔出网线时工作,它只是没有超时,移动到使用HttpWebRequest,现在做的工作。

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(downloadUrl);
request.Timeout = 10000;
request.ReadWriteTimeout = 10000;
var wresp = (HttpWebResponse)request.GetResponse();

using (Stream file = File.OpenWrite(downloadFile))
{
    wresp.GetResponseStream().CopyTo(file);
}

根据kisp解决方案,这是我的编辑版本工作异步:

类WebConnection.cs

internal class WebConnection : WebClient
{
    internal int Timeout { get; set; }

    protected override WebRequest GetWebRequest(Uri Address)
    {
        WebRequest WebReq = base.GetWebRequest(Address);
        WebReq.Timeout = Timeout * 1000 // Seconds
        return WebReq;
    }
}

异步任务

private async Task GetDataAsyncWithTimeout()
{
    await Task.Run(() =>
    {
        using (WebConnection webClient = new WebConnection())
        {
            webClient.Timeout = 5; // Five seconds (the multiplication is in the override)
            webClient.DownloadData("https://www.yourwebsite.com");
        }
    });
} // await GetDataAsyncWithTimeout()

否则,如果你不想使用async:

private void GetDataSyncWithTimeout()
{
    using (WebConnection webClient = new WebConnection())
    {
        webClient.Timeout = 5; // Five seconds (the multiplication is in the override)
        webClient.DownloadData("https://www.yourwebsite.com");
    }
} // GetDataSyncWithTimeout()

对于任何需要WebClient超时工作的异步/任务方法的人,建议的解决方案将不起作用。以下是有效的方法:

public class WebClientWithTimeout : WebClient
{
    //10 secs default
    public int Timeout { get; set; } = 10000;

    //for sync requests
    protected override WebRequest GetWebRequest(Uri uri)
    {
        var w = base.GetWebRequest(uri);
        w.Timeout = Timeout; //10 seconds timeout
        return w;
    }

    //the above will not work for async requests :(
    //let's create a workaround by hiding the method
    //and creating our own version of DownloadStringTaskAsync
    public new async Task<string> DownloadStringTaskAsync(Uri address)
    {
        var t = base.DownloadStringTaskAsync(address);
        if(await Task.WhenAny(t, Task.Delay(Timeout)) != t) //time out!
        {
            CancelAsync();
        }
        return await t;
    }
}

我在博客上写了完整的解决方法

你可以扩展超时:继承原始的WebClient类并重写webrequest getter来设置你自己的超时,如下例所示。

在我的例子中,MyWebClient是一个私有类:

private class MyWebClient : WebClient
{
    protected override WebRequest GetWebRequest(Uri uri)
    {
        WebRequest w = base.GetWebRequest(uri);
        w.Timeout = 20 * 60 * 1000;
        return w;
    }
}

正如Sohnee所说,使用System.Net.HttpWebRequest并设置Timeout属性,而不是使用System.Net.WebClient。

然而,你不能设置一个无限的超时值(它不受支持,试图这样做会抛出一个argumentoutofranceexception)。

我建议首先执行HEAD HTTP请求,检查返回的Content-Length头值,以确定您正在下载的文件中的字节数,然后为后续的GET请求相应地设置超时值,或者简单地指定一个非常长的超时值,您不会期望超过这个超时值。