So ändern Sie das Zeitlimit für ein .NET WebClient-Objekt

229

Ich versuche, die Daten eines Clients (programmgesteuert) auf meinen lokalen Computer herunterzuladen, und sein Webserver ist sehr, sehr langsam, was zu einer Zeitüberschreitung in meinem WebClientObjekt führt.

Hier ist mein Code:

WebClient webClient = new WebClient();

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

Gibt es eine Möglichkeit, ein unendliches Zeitlimit für dieses Objekt festzulegen? Oder wenn nicht, kann mir jemand mit einem Beispiel auf einem alternativen Weg helfen, dies zu tun?

Die URL funktioniert in einem Browser einwandfrei - die Anzeige dauert nur etwa 3 Minuten.

Ryall
quelle

Antworten:

377

Sie können das Zeitlimit verlängern: Erben Sie die ursprüngliche WebClient-Klasse und überschreiben Sie den Webrequest-Getter, um Ihr eigenes Zeitlimit festzulegen, wie im folgenden Beispiel.

MyWebClient war in meinem Fall eine Privatklasse:

private class MyWebClient : WebClient
{
    protected override WebRequest GetWebRequest(Uri uri)
    {
        WebRequest w = base.GetWebRequest(uri);
        w.Timeout = 20 * 60 * 1000;
        return w;
    }
}
kisp
quelle
5
Was ist das Standard-Timeout?
Knocte
23
Das Standardzeitlimit beträgt 100 Sekunden. Obwohl es 30 Sekunden lang zu laufen scheint.
Carter Medlin
2
Ein bisschen einfacher, das Timeout mit einer Zeitspanne festzulegen. TimeSpan.FromSeconds (20) .Milliseconds ... eine großartige Lösung!
Webwires
18
@webwires Man sollte verwenden .TotalMillisecondsund nicht .Milliseconds!
Alexander Galkin
79
Der Name der Klasse sollte lauten: PatientWebClient;)
Jan Willem B
26

Die erste Lösung hat bei mir nicht funktioniert, aber hier ist ein Code, der bei mir funktioniert hat.

    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);
        }
    }
Davinci Jeremie
quelle
21

Sie müssen das Zeitlimit verwenden, HttpWebRequestanstatt es zu WebClientaktivieren, WebClientohne es zu erweitern (obwohl es das verwendet HttpWebRequest). Wenn Sie HttpWebRequeststattdessen die Option verwenden, können Sie das Zeitlimit festlegen.

Fenton
quelle
Dies ist nicht wahr ... Sie können oben sehen, dass Sie WebClient weiterhin verwenden können, obwohl eine benutzerdefinierte Implementierung die WebRequest überschreibt, um das Zeitlimit festzulegen.
DomenicDatti
7
"System.Net.HttpWebRequest.HttpWebRequest ()" ist veraltet: 'Diese API unterstützt die .NET Framework-Infrastruktur und soll nicht direkt aus Ihrem Code verwendet werden "
nützlichBee
3
@usefulBee - weil Sie diesen Konstruktor nicht aufrufen sollten: "Do not use the HttpWebRequest constructor. Use the WebRequest.Create method to initialize new HttpWebRequest objects."von msdn.microsoft.com/en-us/library/… . Siehe auch stackoverflow.com/questions/400565/...
ToolmakerSteve
Nur zur Verdeutlichung: Obwohl dieser spezielle Konstruktor vermieden werden sollte (er ist sowieso nicht mehr Teil neuerer .NET-Versionen), ist es vollkommen in Ordnung, die TimeoutEigenschaft von zu verwenden HttpWebRequest. Es ist in Millisekunden.
Marcel
10

Der w.Timeout-Code konnte nicht funktionieren, wenn das Netzwerkkabel herausgezogen wurde. Es war einfach kein Zeitlimit, wurde auf die Verwendung von HttpWebRequest umgestellt und erledigt den Job jetzt.

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);
}
themullet
quelle
1
Diese Antwort funktioniert gut, aber für alle Interessierten, wenn Sie var wresp = await request.GetResponseAsync();anstelle von var verwenden, erhalten wresp = (HttpWebResponse)request.GetResponse();Sie wieder eine massive Zeitüberschreitung
andrewjboyd
andrewjboyd: weißt du warum GetResponseAsync () nicht funktioniert?
Osexpert
9

Der Vollständigkeit halber ist hier die auf VB portierte Lösung von kisp (Code kann keinem Kommentar hinzugefügt werden)

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
GlennG
quelle
7

Wie Sohnee sagt, verwenden System.Net.HttpWebRequestund setzen Sie die TimeoutEigenschaft, anstatt zu verwenden System.Net.WebClient.

Sie können jedoch keinen unendlichen Zeitlimitwert festlegen (dieser wird nicht unterstützt, und wenn Sie dies versuchen, wird ein Wert ausgelöst ArgumentOutOfRangeException).

Ich würde empfehlen, zuerst eine HEAD-HTTP- Anforderung auszuführen und den zurückgegebenen Content-LengthHeader-Wert zu untersuchen, um die Anzahl der Bytes in der heruntergeladenen Datei zu bestimmen, und dann den Timeout-Wert für die nachfolgende GETAnforderung entsprechend festzulegen oder einfach einen sehr langen Timeout-Wert anzugeben, den Sie möchten Erwarten Sie niemals zu überschreiten.

Dariom
quelle
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
Josh
quelle
5

Für alle, die einen WebClient mit einer Zeitüberschreitung benötigen, die für asynchrone / Task-Methoden funktioniert , funktionieren die vorgeschlagenen Lösungen nicht. Folgendes funktioniert:

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

Ich gebloggt die volle Abhilfe hier

Alex
quelle
4

Verwendung:

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

Klasse:

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

Aber ich empfehle eine modernere Lösung:

using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

public static async Task<string> ReadGetRequestDataAsync(Uri uri, TimeSpan? timeout = null, CancellationToken cancellationToken = default)
{
    using var source = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
    if (timeout != null)
    {
        source.CancelAfter(timeout.Value);
    }

    using var client = new HttpClient();
    using var response = await client.GetAsync(uri, source.Token).ConfigureAwait(false);

    return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
}

Es wird eine OperationCanceledExceptionnach einer Auszeit werfen .

Konstantin S.
quelle
Hat nicht funktioniert, die asynchrone Methode funktioniert immer noch auf unbestimmte Zeit
Alex
Vielleicht ist das Problem anders und Sie müssen ConfigureAwait (false) verwenden?
Konstantin S.
-1

In einigen Fällen muss ein Benutzeragent zu den Headern hinzugefügt werden:

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)";

Dies war die Lösung für meinen Fall.

Anerkennung:

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

Antoine
quelle