如何更改.NET WebClient对象的超时

时间:2009-11-24 11:59:59

标签: c# .net file download webclient

我正在尝试将客户端的数据下载到本地计算机(以编程方式),并且他们的网络服务器非常非常慢,导致我的WebClient对象超时。

这是我的代码:

WebClient webClient = new WebClient();

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

有没有办法在这个对象上设置无限超时?或者,如果没有,任何人都可以通过另一种方式帮助我做一个例子吗?

网址在浏览器中正常运行 - 只需3分钟即可显示。

12 个答案:

答案 0 :(得分:355)

您可以延长超时:继承原始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;
    }
}

答案 1 :(得分:20)

您需要使用HttpWebRequest而不是WebClient,因为您无法在WebClient上设置超时而不对其进行扩展(即使它使用HttpWebRequest)。使用HttpWebRequest代替将允许您设置超时。

答案 2 :(得分:19)

第一个解决方案对我不起作用,但这里有一些代码对我有用。

    private class WebClient : System.Net.WebClient
    {
        public int Timeout { get; set; }

        protected override WebRequest GetWebRequest(Uri uri)
        {
            WebRequest lWebRequest = base.GetWebRequest(uri);
            lWebRequest.Timeout = Timeout;
            ((HttpWebRequest)lWebRequest).ReadWriteTimeout = Timeout;
            return lWebRequest;
        }
    }

    private string GetRequest(string aURL)
    {
        using (var lWebClient = new WebClient())
        {
            lWebClient.Timeout = 600 * 60 * 1000;
            return lWebClient.DownloadString(aURL);
        }
    }

答案 3 :(得分:10)

拔出网线时无法使w.Timeout代码正常工作,它只是没有超时,转而使用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);
}

答案 4 :(得分:8)

为了完整性,这里的kisp解决方案移植到VB(无法在注释中添加代码)

Namespace Utils

''' <summary>
''' Subclass of WebClient to provide access to the timeout property
''' </summary>
Public Class WebClient
    Inherits System.Net.WebClient

    Private _TimeoutMS As Integer = 0

    Public Sub New()
        MyBase.New()
    End Sub
    Public Sub New(ByVal TimeoutMS As Integer)
        MyBase.New()
        _TimeoutMS = TimeoutMS
    End Sub
    ''' <summary>
    ''' Set the web call timeout in Milliseconds
    ''' </summary>
    ''' <value></value>
    Public WriteOnly Property setTimeout() As Integer
        Set(ByVal value As Integer)
            _TimeoutMS = value
        End Set
    End Property


    Protected Overrides Function GetWebRequest(ByVal address As System.Uri) As System.Net.WebRequest
        Dim w As System.Net.WebRequest = MyBase.GetWebRequest(address)
        If _TimeoutMS <> 0 Then
            w.Timeout = _TimeoutMS
        End If
        Return w
    End Function

End Class

End Namespace

答案 5 :(得分:7)

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

但是,您无法设置无限超时值(它不受支持,尝试这样做会抛出ArgumentOutOfRangeException)。

我建议先执行HEAD HTTP请求并检查返回的Content-Length标头值,以确定您正在下载的文件中的字节数,然后相应地为后续{设置超时值{1}}请求或只是指定一个你永远不会超过的超长超时值。

答案 6 :(得分:7)

'CORRECTED VERSION OF LAST FUNCTION IN VISUAL BASIC BY GLENNG

Protected Overrides Function GetWebRequest(ByVal address As System.Uri) As System.Net.WebRequest
            Dim w As System.Net.WebRequest = MyBase.GetWebRequest(address)
            If _TimeoutMS <> 0 Then
                w.Timeout = _TimeoutMS
            End If
            Return w  '<<< NOTICE: MyBase.GetWebRequest(address) DOES NOT WORK >>>
        End Function

答案 7 :(得分: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;
    }
}

我在博客中介绍了完整的解决方法here

答案 8 :(得分:2)

用法:

using (var client = new TimeoutWebClient(TimeSpan.FromSeconds(10)))
{
    return await client.DownloadStringTaskAsync(url);
}

班级:

using System;
using System.Net;

namespace Utilities
{
    public class TimeoutWebClient : WebClient
    {
        public TimeSpan Timeout { get; set; }

        public TimeoutWebClient(TimeSpan timeout)
        {
            Timeout = timeout;
        }

        protected override WebRequest GetWebRequest(Uri uri)
        {
            var request = base.GetWebRequest(uri);
            if (request == null)
            {
                return null;
            }

            var timeoutInMilliseconds = (int) Timeout.TotalMilliseconds;

            request.Timeout = timeoutInMilliseconds;
            if (request is HttpWebRequest httpWebRequest)
            {
                httpWebRequest.ReadWriteTimeout = timeoutInMilliseconds;
            }

            return request;
        }
    }
}

答案 9 :(得分:0)

昨天我不得不解决这个问题,而且我最终还写了我的自定义扩展类。

通过查看下面的代码并将其与可接受的答案进行比较,您可以看到,我尝试对建议进行更多的调整,以使类更通用:这样,您可以在在使用使用内部WebRequest处理程序的方法之前,先实例化对象。

using System;
using System.Net;

namespace Ryadel.Components.Web
{
    /// <summary>
    /// An extension of the standard System.Net.WebClient
    /// featuring a customizable constructor and [Timeout] property.
    /// </summary>
    public class RyadelWebClient : WebClient
    {
        /// <summary>
        /// Default constructor (30000 ms timeout)
        /// NOTE: timeout can be changed later on using the [Timeout] property.
        /// </summary>
        public RyadelWebClient() : this(30000) { }

        /// <summary>
        /// Constructor with customizable timeout
        /// </summary>
        /// <param name="timeout">
        /// Web request timeout (in milliseconds)
        /// </param>
        public RyadelWebClient(int timeout)
        {
            Timeout = timeout;
        }

        #region Methods
        protected override WebRequest GetWebRequest(Uri uri)
        {
            WebRequest w = base.GetWebRequest(uri);
            w.Timeout = Timeout;
            ((HttpWebRequest)w).ReadWriteTimeout = Timeout;
            return w;
        }
        #endregion

        /// <summary>
        /// Web request timeout (in milliseconds)
        /// </summary>
        public int Timeout { get; set; }
    }
}

在我在那里的同时,我也借此机会将默认的Timeout值降低为30秒,因为100对我来说似乎太多了。

如果您需要有关该类或如何使用它的其他信息,请查看我在博客上写的this post

答案 10 :(得分:0)

根据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
            webClient.DownloadData("https://www.yourwebsite.com");
        }
    });
} // await GetDataAsyncWithTimeout()

否则,如果您不想使用异步:

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

答案 11 :(得分:-1)

在某些情况下,有必要将用户代理添加到标题中:

WebClient myWebClient = new WebClient();
myWebClient.DownloadFile(myStringWebResource, fileName);
myWebClient.Headers["User-Agent"] = "Mozilla/4.0 (Compatible; Windows NT 5.1; MSIE 6.0) (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)";

这是我的案子的解决方案。

信用:

http://genjurosdojo.blogspot.com/2012/10/the-remote-server-returned-error-504.html