7

I'm downloading a file using System.Net.WebClient.DownloadFileAsync(). The only reason for using the async version is to show the progress of the download. My code execution may not continue before 100% have been reached. Currently I'm using a busy-wait (see code) but I wonder if there is a smarter way to do it.

using(WebClient oWebClient = new WebClient())
{
    oWebClient.Encoding = System.Text.Encoding.UTF8;
    long lReceived = 0;
    long lTotal = 0;
    // Set up a delegate to watch download progress.
    oWebClient.DownloadProgressChanged += delegate(object sender, DownloadProgressChangedEventArgs e)
    {
        Console.WriteLine(e.ProgressPercentage + "% (" + e.BytesReceived / 1024f + "kb of " + e.TotalBytesToReceive / 1024f + "kb)");
            // Assign to outer variables to allow busy-wait to check.
        lReceived = e.BytesReceived;
        lTotal = e.TotalBytesToReceive;
    };
    oWebClient.DownloadFileAsync(new Uri(sUrl + "?" + sPostData), sTempFile);
    while(lReceived == 0 || lReceived != lTotal)
    {
           // Busy wait.
        Thread.Sleep(1000);
    }
}
Krumelur
  • 32,180
  • 27
  • 124
  • 263

3 Answers3

9
using(WebClient oWebClient = new WebClient())
{
    // use an event for waiting, rather than a Thread.Sleep() loop.
    var notifier = new AutoResetEvent(false);

    oWebClient.Encoding = System.Text.Encoding.UTF8;
    long lReceived = 0;
    long lTotal = 0;
    // Set up a delegate to watch download progress.
    oWebClient.DownloadProgressChanged += delegate(object sender, DownloadProgressChangedEventArgs e)
    {
        Console.WriteLine(e.ProgressPercentage + "% (" + e.BytesReceived / 1024f + "kb of " + e.TotalBytesToReceive / 1024f + "kb)");
        // Assign to outer variables to allow busy-wait to check.
        lReceived = e.BytesReceived;
        lTotal = e.TotalBytesToReceive;

        // Indicate that things are done
        if(lReceived >= lTotal) notifier.Set();
    };

    oWebClient.DownloadFileAsync(new Uri(sUrl + "?" + sPostData), sTempFile);

    // wait for signal to proceed
    notifier.WaitOne();
}
OJ.
  • 28,944
  • 5
  • 56
  • 71
  • Don't ya hate it when you post your answer _after_ another one has been accepted :P – OJ. Apr 17 '11 at 01:37
  • Sorry! I know that myself. Sometimes SO shows a warning while you edit that the somebody else has answered but I don't get it everytime. – Krumelur Apr 17 '11 at 17:01
  • Don't apologise mate :-) you got the answer and it was right. that's the way it goes! – OJ. Apr 17 '11 at 21:16
  • This one is better too because it has a code example...should be the answer +1 – FrostyFire Jun 02 '15 at 20:23
5

Use an AutoResetEvent. Call its Set() method in a DownloadFileCompleted event handler, its WaitOne() method after the DownloadFileAsync() call.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
0
using(WebClient oWebClient = new WebClient())
{
    // use an event for waiting, rather than a Thread.Sleep() loop.
    var notifier = new AutoResetEvent(false);
    oWebClient.Encoding = System.Text.Encoding.UTF8;
    long lReceived = 0;
    long lTotal = 0;

    // Set up a delegate to watch download progress.
    oWebClient.DownloadProgressChanged += delegate(object sender, DownloadProgressChangedEventArgs e)
    {
        Console.WriteLine(e.ProgressPercentage + "% (" + e.BytesReceived / 1024f + "kb of " + e.TotalBytesToReceive / 1024f + "kb)");
        // Assign to outer variables to allow busy-wait to check.
        lReceived = e.BytesReceived;
        lTotal = e.TotalBytesToReceive;
    };

    // Set a delegate to watch for when the download is complete
      oWebClient.OpenReadCompleted += delegate(object sender, OpenReadCompletedEventArgs e)
    {
         // Indicate that things are done
         notifier.Set();
    };

    oWebClient.DownloadFileAsync(new Uri(sUrl + "?" + sPostData), sTempFile);

    // wait for signal to proceed
    notifier.WaitOne();
}

I have expanded on @OJ's answer to instead set the notifier when the OpenReadCompleted fires. This will stop the thread from hanging if the file errors during download.

Ref: WebClient DownloadFileAsync hangs

Community
  • 1
  • 1