HttpClient - 下载前下载文件的大小

时间:2014-06-22 08:28:59

标签: c# windows-runtime windows-store-apps dotnet-httpclient

我正在使用进度条实现文件下载。我正在使用 IAsyncOperationWithProgress 解决此问题,具体来说是this code。它工作得很好,但我只收到接收/下载的字节数。但我需要计算百分比来显示进度。这意味着我需要知道下载开始时的总字节数,我还没有找到如何有效地做到这一点。

以下代码可解析进度报告。我尝试使用 responseStream.Length 获取流长度,但错误"此流不支持搜索操作。"被扔了。

static async Task<byte[]> GetByteArratTaskProvider(Task<HttpResponseMessage> httpOperation, CancellationToken token, IProgress<int> progressCallback)
        {
            int offset = 0;
            int streamLength = 0;
            var result = new List<byte>();

            var responseBuffer = new byte[500];

            // Execute the http request and get the initial response
            // NOTE: We might receive a network error here
            var httpInitialResponse = await httpOperation;

            using (var responseStream = await httpInitialResponse.Content.ReadAsStreamAsync())
            {
                int read;

                do
                {
                    if (token.IsCancellationRequested)
                    {
                        token.ThrowIfCancellationRequested();
                    }

                    read = await responseStream.ReadAsync(responseBuffer, 0, responseBuffer.Length);

                    result.AddRange(responseBuffer);

                    offset += read;
                    // here I want to send percents of downloaded data
                    // offset / (totalSize / 100)
                    progressCallback.Report(offset);

                } while (read != 0);
            }

            return result.ToArray();
        }

你知道如何处理这个问题吗?或者您是否有其他方式如何通过HttpClient下载具有进度报告的文件?我也尝试使用 BackgroundDownloader ,但这对我来说还不够。谢谢。

1 个答案:

答案 0 :(得分:6)

您可以查看服务器返回的Content-Length标头的值,该标头存储在httpInitialResponse.Content.Headers的案例中。您必须使用相应的键(即“Content-Length”)

在集合中找到标题

你可以这样做:

int length = int.Parse(httpInitialResponse.Content.Headers.First(h => h.Key.Equals("Content-Length")).Value.First());

(您必须首先确保服务器已发送Content-Length标头,否则上面的行将因异常而失败)

您的代码如下:

static async Task<byte[]> GetByteArrayTaskProvider(Task<HttpResponseMessage> httpOperation, CancellationToken token, IProgress<int> progressCallback)
{
    int offset = 0;
    int streamLength = 0;
    var result = new List<byte>();

    var responseBuffer = new byte[500];

    // Execute the http request and get the initial response
    // NOTE: We might receive a network error here
    var httpInitialResponse = await httpOperation;
    var totalValueAsString = httpInitialResponse.Content.Headers.SingleOrDefault(h => h.Key.Equals("Content-Length"))?.Value?.First());
    int? totalValue = totalValueAsString != null ? int.Parse(totalValueAsString) : null;

    using (var responseStream = await httpInitialResponse.Content.ReadAsStreamAsync())
    {
       int read;
       do
       {
           if (token.IsCancellationRequested)
           {
              token.ThrowIfCancellationRequested();
           }

           read = await responseStream.ReadAsync(responseBuffer, 0, responseBuffer.Length);
           result.AddRange(responseBuffer);

           offset += read;
           if (totalSize.HasValue)
           {
              progressCallback.Report(offset * 100 / totalSize);
           }
           //for else you could send back the offset, but the code would become to complex in this method and outside of it. The logic for "supports progress reporting" should be somewhere else, to keep methods simple and non-multi-purpose (I would create a method for with bytes progress and another for percentage progress)
       } while (read != 0);
    }
    return result.ToArray();
}