Как изменить таймаут на объекте .NET WebClient

Я пытаюсь загрузить клиентские данные на свой локальный компьютер (программно), и их веб-сервер очень и очень медленный, что вызывает тайм-аут в моем WebClient объекте.

Вот мой код:

WebClient webClient = new WebClient();

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

Есть ли способ установить бесконечный тайм-аут на этом объекте? Или, если нет, кто-нибудь может мне помочь с примером для альтернативного способа сделать это?

URL-адрес отлично работает в браузере - для отображения требуется всего около 3 минут.

Ответ 1

Вы можете продлить тайм-аут: наследовать исходный класс WebClient и переопределить метод получения веб-запросов, чтобы установить собственный тайм-аут, как в следующем примере.

MyWebClient был закрытым классом в моем случае:

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

Ответ 2

Первое решение не сработало для меня, но вот какой-то код, который работал у меня.

    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

Вам нужно использовать HttpWebRequest, а не WebClient, поскольку вы не можете установить таймаут на WebClient, не расширяя его (хотя он использует HttpWebRequest). Использование HttpWebRequest вместо этого позволит вам установить тайм-аут.

Ответ 4

Не удалось заставить код 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);
}

Ответ 5

Для полноты, здесь решение 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

Ответ 6

Как говорит Sohnee, используя System.Net.HttpWebRequest и устанавливая свойство Timeout вместо System.Net.WebClient.

Однако вы не можете установить бесконечное значение таймаута (оно не поддерживается, и попытка сделать это приведет к выбросу ArgumentOutOfRangeException).

Я бы порекомендовал сначала выполнить HEAD HTTP запрос и изучить значение заголовка Content-Length, возвращаемое для определения количества байтов в файле вы загружаете, а затем устанавливаете значение тайм-аута соответственно для последующего запроса GET или просто указываете очень длинное значение таймаута, которое вы никогда не ожидаете превышать.

Ответ 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

Ответ 8

Использование:

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

Для тех, кому нужен 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 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;
    }
}

Я в блоге о полном обходе здесь