HttpClientを使ってファイルをダウンロードする時に進捗を表示することを実現します.

7695 ワード

frame efforkでファイルをダウンロードして進捗を表示するには、WebClientクラスを使うのが一番簡単です.DownloadProgress Changedイベントを購読すればいいです.
残念ながら、WebCientは.net standardに含まれていません.net standardの中でhttpネットの要求を行って、私達が使うのはもっと多いのがHttpClientです.また、UWPの中にもHttpClientがあります.使い方は似ていますが、名前空間は違っています.またUWPのはダウンロードの進捗をサポートしています.ここでは詳しく説明しません.
ファイルをダウンロードするなら、私たちはHttpClientのGetByteArayAryncという方法を使います.ダウンロードの進捗を実現するには、どうすればいいですか?よく言われていますが、だめなら一重に包んでください.ここでは拡張方法を書いて、次のように定義します.
public static class HttpClientExtensions
{
    public static Task<byte[]> GetByteArrayAsync(this HttpClient client, Uri requestUri, IProgress progress, CancellationToken cancellationToken)
    {
        throw new NotImplementedException();
    }
}
その中のHttpDownloadPrograssは自分で定義した構造体です.コードは以下の通りです.
public struct HttpDownloadProgress
{
    public ulong BytesReceived { get; set; }

    public ulong? TotalBytesToReceive { get; set; }
}
Bytes Receivedはダウンロード済みのバイト数を表し、TotalBytes ToReceiveはダウンロードが必要なバイト数を表しています.httpの応答ヘッダは必ずしも長さに戻るとは限らないので、ここでは空きがあります.
http応答ヘッダからcontent-lengthを取得する必要があるので、HttpClient自身のGetByteArayAryncは実現できません.GetAryncを使用する方法に転向したいです.Get Aryncこの方法には重荷重があります.https://docs.microsoft.com/zh-cn/dotnet/api/system.net.http.httpclient.getasync#System_Net_Http_Http Client_Get Aync_System_同前System_Net_Http_HttpCompleetion Option_System_Threading_CancellationToken_第二のパラメータは列挙されていますが、いつごろからレスリングが得られますか?需要に応じて、私達はここでHttpCopletion Option.ReponseHeaders Readを使うべきです.
またHttpClientのソースコードもGithubで見られます.https://github.com/dotnet/corefx/blob/d69d441dfb0710c2a34155c7c4745db357b14c96/src/System.Net.Http/src/System/Net/Http/HttpClient.cs GetByteArayAryncの実現を参照することができます.
考えた結果、次のコードが書けます.
public static class HttpClientExtensions
{
    private const int BufferSize = 8192;

    public static async Task<byte[]> GetByteArrayAsync(this HttpClient client, Uri requestUri, IProgress progress, CancellationToken cancellationToken)
    {
        if (client == null)
        {
            throw new ArgumentNullException(nameof(client));
        }

        using (var responseMessage = await client.GetAsync(requestUri, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false))
        {
            responseMessage.EnsureSuccessStatusCode();

            var content = responseMessage.Content;
            if (content == null)
            {
                return Array.Empty<byte>();
            }

            var headers = content.Headers;
            var contentLength = headers.ContentLength;
            using (var responseStream = await content.ReadAsStreamAsync().ConfigureAwait(false))
            {
                var buffer = new byte[BufferSize];
                int bytesRead;
                var bytes = new List<byte>();

                var downloadProgress = new HttpDownloadProgress();
                if (contentLength.HasValue)
                {
                    downloadProgress.TotalBytesToReceive = (ulong)contentLength.Value;
                }
                progress?.Report(downloadProgress);

                while ((bytesRead = await responseStream.ReadAsync(buffer, 0, BufferSize, cancellationToken).ConfigureAwait(false)) > 0)
                {
                    bytes.AddRange(buffer.Take(bytesRead));

                    downloadProgress.BytesReceived += (ulong)bytesRead;
                    progress?.Report(downloadProgress);
                }

                return bytes.ToArray();
            }
        }
    }
}
ここではバッファエリアを8192バイト(8 KB)に設定します.読み取り8 KBごとにダウンロードの進捗度を報告します.もちろん、視聴者の皆様もこの値を小さくしてもいいです.効果はもっといいですが、相対的な性能はちょっと悪いです.また、ここではReportの周波数が高いため、HttpDownloadPrograssはclassを使うのに適していません.
自分のデモの効果図を以下に示します.