93

I'm using webClient.DownloadFile() to download a file can I set a timeout for this so that it won't take so long if it can't access the file?

abatishchev
  • 98,240
  • 88
  • 296
  • 433
UnkwnTech
  • 88,102
  • 65
  • 184
  • 229

4 Answers4

261

My answer comes from here

You can make a derived class, which will set the timeout property of the base WebRequest class:

using System;
using System.Net;

public class WebDownload : WebClient
{
    /// <summary>
    /// Time in milliseconds
    /// </summary>
    public int Timeout { get; set; }

    public WebDownload() : this(60000) { }

    public WebDownload(int timeout)
    {
        this.Timeout = timeout;
    }

    protected override WebRequest GetWebRequest(Uri address)
    {
        var request = base.GetWebRequest(address);
        if (request != null)
        {
            request.Timeout = this.Timeout;
        }
        return request;
    }
}

and you can use it just like the base WebClient class.

Kirk Woll
  • 76,112
  • 22
  • 180
  • 195
Beniamin
  • 3,198
  • 2
  • 19
  • 17
  • 3
    Just incase someone else comes across this helpful code I had to set the timeout before calling base.GetWebRequest(address) – Darthtong Apr 16 '12 at 16:42
  • Resharper complains about a possible null value for "result" and suggests a null check before setting the Timeout value to the WebRequest. Looking at the decompiled code, it seems impossible unless you provide a custom WebRequestModules in your web.config, but for such an upvoted answer, I would add it just in case. – Kevin Coulombe May 10 '13 at 20:56
  • I am getting the error at this line `request.Timeout`. Error msg `'System.Net.WebRequest' does not contain a definition for 'Timeout' and no extension method 'Timeout' accepting a first argument of type 'System.Net.WebRequest' could be found (are you missing a using directive or an assembly reference?) `, what am I missing? – Eric Jul 03 '13 at 14:18
  • 1
    @Eric: I've added `using` directives that are used by this code snippet. – Beniamin Jul 08 '13 at 08:43
  • This soultion does not work with DownloadStringAsync. Is there something similiar, or only way is to use timer? – titol Aug 08 '15 at 09:37
  • 1
    @titol: use HttpClient rather than WebClient. – abatishchev Sep 13 '15 at 15:10
  • Yes, I know that HttpClient could do that, but i was curious if there are way to do this with WebClient. – titol Sep 13 '15 at 18:45
  • @Darthtong how? as `base.GetWebRequest(address)` returns the object on which to set the timeout. – Cee McSharpface Feb 18 '20 at 12:56
  • @titol https://stackoverflow.com/questions/32320683/does-webclient-downloadfiletaskasync-never-actually-timeout – Beniamin Mar 15 '20 at 12:45
42

Try WebClient.DownloadFileAsync(). You can call CancelAsync() by timer with your own timeout.

abatishchev
  • 98,240
  • 88
  • 296
  • 433
  • 2
    I don't want using timer or stopwatch. I want some built-in hack or api approach. Using timer/stopwatch costs me additional thread for watching, while this feature maybe already implemented, so why re-inventing the wheel –  Sep 13 '15 at 14:01
  • @Kilanny: then go with the solution from another answer. Or use HttpClient and set the Timeout property. Also please note this answer is from 2009. – abatishchev Sep 13 '15 at 15:08
  • 8
    in .Net 4.5+ you can also use `var taskDownload = client.DownloadFileTaskAsync(new Uri("http://localhost/folder"),"filename")` and then `taskDownload.Wait(TimeSpan.FromSeconds(5));` – itsho Feb 03 '16 at 20:37
3

Assuming you wanted to do this synchronously, using the WebClient.OpenRead(...) method and setting the timeout on the Stream that it returns will give you the desired result:

using (var webClient = new WebClient())
using (var stream = webClient.OpenRead(streamingUri))
{
     if (stream != null)
     {
          stream.ReadTimeout = Timeout.Infinite;
          using (var reader = new StreamReader(stream, Encoding.UTF8, false))
          {
               string line;
               while ((line = reader.ReadLine()) != null)
               {
                    if (line != String.Empty)
                    {
                        Console.WriteLine("Count {0}", count++);
                    }
                    Console.WriteLine(line);
               }
          }
     }
}

Deriving from WebClient and overriding GetWebRequest(...) to set the timeout @Beniamin suggested, didn't work for me as, but this did.

jeffrymorris
  • 464
  • 3
  • 3
  • @jeffymorris didn't work for me. I still get WebException saying "the request was aborted - the operation has timed out" even if I specify `stream.ReadTimeout` bigger than it actually took to execute the request – chester89 Jun 27 '17 at 07:40
  • @jeffymoris on the other, the solution with webclient subclass didn't work either, so it's probably a problem on the server side – chester89 Jun 29 '17 at 13:59
0

A lot of people make use of using(...) for the WebClient. Yes, WebClient implements IDisposable but this can cause socket exhaustion if you do it in bulk: https://www.aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

user2029101
  • 116
  • 9