How to implement progress reporting for Portable HttpClient

TheBlueSky picture TheBlueSky · Jan 16, 2014 · Viewed 13k times · Source

I'm writing a library with intentions to use it in desktop (.Net 4.0 and up), phone (WP 7.5 and up) and Windows Store (Windows 8 and up) apps.

The library has the capability to download files from the Internet using Portable HttpClient library, and report the progress of the download.

I search around here and the rest of the internet for documentations and code sample/guidelines on how to implement the progress reporting, and this search led me to nowhere.

Does anyone has an article, documentation, guideline, code sample or whatever to help me achieve this?

Answer

TheBlueSky picture TheBlueSky · Feb 8, 2014

I wrote the following code to implement progress reporting. The code supports all the platforms I wanted; however, you need to reference the following NuGet packages:

  • Microsoft.Net.Http
  • Microsoft.Bcl.Async

Here is the code:

public async Task DownloadFileAsync(string url, IProgress<double> progress, CancellationToken token)
{
    var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token);

    if (!response.IsSuccessStatusCode)
    {
        throw new Exception(string.Format("The request returned with HTTP status code {0}", response.StatusCode));
    }

    var total = response.Content.Headers.ContentLength.HasValue ? response.Content.Headers.ContentLength.Value : -1L;
    var canReportProgress = total != -1 && progress != null;

    using (var stream = await response.Content.ReadAsStreamAsync())
    {
        var totalRead = 0L;
        var buffer = new byte[4096];
        var isMoreToRead = true;

        do
        {
            token.ThrowIfCancellationRequested();

            var read = await stream.ReadAsync(buffer, 0, buffer.Length, token);

            if (read == 0)
            {
                isMoreToRead = false;
            }
            else
            {
                var data = new byte[read];
                buffer.ToList().CopyTo(0, data, 0, read);

                // TODO: put here the code to write the file to disk

                totalRead += read;

                if (canReportProgress)
                {
                    progress.Report((totalRead * 1d) / (total * 1d) * 100);
                }
            }
        } while (isMoreToRead);
    }
}

The using it is as simple as:

var progress = new Microsoft.Progress<double>();
progress.ProgressChanged += (sender, value) => System.Console.Write("\r%{0:N0}", value);

var cancellationToken = new CancellationTokenSource();

await DownloadFileAsync("http://www.dotpdn.com/files/Paint.NET.3.5.11.Install.zip", progress, cancellationToken.Token);