2014-01-16 81 views
6

デスクトップ(.Net 4.0以降)、電話(WP 7.5以降)、Windowsストア(Windows 8以降)のアプリケーションで使用するためのライブラリを作成しています。Portable HttpClientの進捗報告の実装方法

ライブラリには、ポータブルHttpClientライブラリを使用してインターネットからファイルをダウンロードし、ダウンロードの進行状況を報告する機能があります。

私はここで、インターネットのその他の部分では、進捗状況報告の実装方法に関するドキュメントとコードサンプル/ガイドラインを検索しています。この検索は私をどこにも導きませんでした。

記事、ドキュメント、ガイドライン、コードサンプルなどは誰でも持っていますか?

答えて

17

進捗報告を実装するために、次のコードを書いています。このコードは、私が望むすべてのプラットフォームをサポートしています。ただし、次のNuGetパッケージを参照する必要があります。

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

ここではコードです:

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

使用は、

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); 
+0

これはネクロコメントのようなものですが、このアプローチを使用してタイミングの問題にぶつかりました。そこに進捗状況が非常に多く、ダウンロードの実際の進捗に遅れが生じました。報告された進捗状況よりもはるかに高速にダウンロードされています。 (私はちょうど毎秒報告するためにストップウォッチを使用することになった) – Dynde

+1

これを引き起こしたかもしれないか分からない。私はそれが私のシナリオでうまくいっていることを発見した4096バイトのバッファサイズを使用します。 – TheBlueSky

1

ストリームから読み込み中は、HttpCompletionOption.ResponseHeadersReadを指定してストリームを取得し、進行状況をレポートできます。 this similar questionを参照してください。

+1

多分私の質問に対する答えではありません。しかし、リンクのおかげで、それは私が答えを見つけるのを助けた。 – TheBlueSky

0

// TODO:ファイルをディスクに書き込むコードをここに入れます

@TheBlueSky:そうですか?

public static async Task DownloadFileAsync(this HttpClient client, string url, string fileName, IProgress<double> progress, CancellationToken ct, int bufferSize = 4096, 
               bool overwrite = true) 
    { 
     #region Checking parameters 

     if (url.Length == 0) throw new ArgumentException("The parameter value can not be a zero-length string.", nameof(url)); 
     if (fileName == null) throw new ArgumentNullException(nameof(fileName), "File is null"); 
     if (fileName.Length == 0) throw new ArgumentException("The parameter value can not be a zero-length string.", nameof(fileName)); 

     #endregion 

     var uri  = new Uri(url, UriKind.Absolute); 
     var response = await client.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead, ct).ConfigureAwait(false); 
     if (!response.IsSuccessStatusCode) throw new Exception($"The request returned with HTTP status code {response.StatusCode}"); 

     var pathName   = Path.GetFullPath(fileName); 
     var total    = response.Content.Headers.ContentLength ?? -1L; 
     var canReportProgress = total != -1 && progress != null; 

     try 
     { 
      var stream  = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); 
      var totalRead = 0L; 
      var buffer  = new byte[bufferSize]; 
      var isMoreToRead = true; 
      do 
      { 
       ct.ThrowIfCancellationRequested(); 
       var read = await stream.ReadAsync(buffer, 0, buffer.Length, ct).ConfigureAwait(false); 

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

        using (var fileStream = new FileStream(pathName, overwrite ? FileMode.Create : FileMode.CreateNew, FileAccess.Write, FileShare.None, bufferSize, true)) 
        { 
         await fileStream.WriteAsync(data, 0, read, ct).ConfigureAwait(false); 
        } 

        totalRead += read; 
        if (canReportProgress) progress.Report(totalRead * 1d/(total * 1d) * 100); 
       } 
      } while (isMoreToRead); 
     } 
     catch (OutOfMemoryException e) 
     { 
      throw new OutOfMemoryException($"Set buffer size is large too. {e.Message}"); 
     } 
     catch (Exception e) 
     { 
      throw new Exception(e.Message); 
     } 
    } 
関連する問題