实现在 .net 中使用 HttpClient 下载文件时显示进度
在 .net framework 中,要实现下载文件并显示进度的话,最简单的做法是使用 WebClient 类。订阅 DownloadProgressChanged 事件就行了。
但是很可惜,WebClient 并不包含在 .net standard 当中。在 .net standard 中,要进行 http 网络请求,我们用得更多的是 HttpClient。另外还要注意的是,UWP 中也有一个 HttpClient,虽然用法差不多,但是命名空间是不一样的,而且 UWP 的是可以支持获取下载进度的,这里就不再细说。
如果要下载文件,我们会使用到 HttpClient 的 GetByteArrayAsync 这个方法。要实现下载进度,那要怎么办呢?俗话说,不行就包一层。这里我们写个扩展方法,定义如下:
public static class HttpClientExtensions
{
public static Task<byte[]> GetByteArrayAsync(this HttpClient client, Uri requestUri, IProgress<HttpDownloadProgress> progress, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
其中 HttpDownloadProgress 是我自己定义的结构体(不使用类的原因我下面再说),代码如下:
public struct HttpDownloadProgress
{
public ulong BytesReceived { get; set; } public ulong? TotalBytesToReceive { get; set; }
}
BytesReceived 代表已经下载的字节数,TotalBytesToReceive 代表需要下载的字节数,因为 http 的响应头不一定会返回长度(content-length),所以这里设置为可空。
由于我们需要从 http 响应头获取到 content-length,而 HttpClient 自身的 GetByteArrayAsync 并没有办法实现,我们需要转向使用 GetAsync 这个方法。GetAsync 这个方法有一个重载 https://docs.microsoft.com/zh-cn/dotnet/api/system.net.http.httpclient.getasync#System_Net_Http_HttpClient_GetAsync_System_Uri_System_Net_Http_HttpCompletionOption_System_Threading_CancellationToken_ 它的第二个参数是一个枚举,代表是什么时候可以得到 response。按照需求,我们这里应该使用 HttpCompletionOption.ResponseHeadersRead 这个。
另外 HttpClient 的源码也可以在 Github 上看得到。https://github.com/dotnet/corefx/blob/d69d441dfb0710c2a34155c7c4745db357b14c96/src/System.Net.Http/src/System/Net/Http/HttpClient.cs 我们可以参考一下 GetByteArrayAsync 的实现。
经过思考,可以写出下面的代码:
public static class HttpClientExtensions
{
private const int BufferSize = ; public static async Task<byte[]> GetByteArrayAsync(this HttpClient client, Uri requestUri, IProgress<HttpDownloadProgress> 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, , BufferSize, cancellationToken).ConfigureAwait(false)) > )
{
bytes.AddRange(buffer.Take(bytesRead)); downloadProgress.BytesReceived += (ulong)bytesRead;
progress?.Report(downloadProgress);
} return bytes.ToArray();
}
}
}
}
这里我将缓冲区设置为 8192 字节(8 KB),相当于每读取 8 KB 就汇报一次下载进度,当然各位看官也可以把这个值调小,这样效果会更好,但相对的性能就差一些。同时也因为这里 Report 的频率是比较高的,因此 HttpDownloadProgress 不适合用 class(否则 GC 会压力相当大)。
下面我自己的 Demo 的效果图:

实现在 .net 中使用 HttpClient 下载文件时显示进度的更多相关文章
- dotnet 通过 HttpClient 下载文件同时报告进度的方法
本文告诉大家一个简单的方法通过 HttpClient 下载文件,同时报告下载进度 通过 HttpClient 的 ContentLength 很多时候都可以拿到下载的内容的长度,通过 ReadAsyn ...
- 使用libcurl开源库和Duilib做的下载文件并显示进度条的小工具
转载:http://blog.csdn.net/mfcing/article/details/43603525 转载:http://blog.csdn.net/infoworld/article/de ...
- [c#]WebClient异步下载文件并显示进度
摘要 在项目开发中经常会用到下载文件,这里使用winform实现了一个带进度条的例子. 一个例子 using System; using System.Collections.Generic; usi ...
- C# Winform下载文件并显示进度条
private void btnDown_Click(object sender, EventArgs e) { DownloadFile("http://localhost:1928/We ...
- Winform下载文件并显示进度条
本来是要研究怎样判断下载完成,结果找到这个方法,可以在这个方法完成之后提示下载完成. 代码如下: using System; using System.Collections.Generic; usi ...
- 通过HttpUrlConnection下载文件并显示进度条
实现效果: 核心下载块: int count = 0; URL url = new URL("http://hezuo.downxunlei.com/xunlei_hezuo/thunder ...
- 在WebView中加载HTML页面时显示进度对话框的方法
webView.setWebViewClient(new WebViewClient(){ ProgressDialog prDialog; @Overri ...
- android中使用Http下载文件并保存到本地SD卡
1.AndroidMainfest.xml中设置权限 <uses-permission android:name="android.permission.INTERNET"& ...
- C#中解决Response.AddHeader("Content-Disposition", "attachment; filename=" + filename)下载文件时文件名乱码的问题
问题:下载文件时文件名乱码怎么解决? 在C#写后台代码过程中,经常遇到下载文件出现文件名乱码的问题,在网上找了很多方法,总是存在浏览器不兼容的问题,当IE浏览器不乱码时,火狐浏览器就会乱码,后来经过反 ...
随机推荐
- 11.1 Android显示系统框架_framebuffer原理及改进
1. Android显示系统框架Android Graphic UI with GPU Hardware Accelerationhttps://community.nxp.com/docs/DOC- ...
- 前端css常用的选择小汇
要使用css对HTML页面中的元素实现一对一,一对多或者多对一的控制,这就需要用到CSS选择器.选择器就是选择器用来指定样式的作用范围. 类选择器: 类选择器在css中比较常见,首先要在普通标签中设置 ...
- ERROR:column "rolcatupdate" does not exist
1.错误描写叙述 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/ ...
- 一次修复IncrediBuild Coordinator服务的经历
作者:朱金灿 来源:http://blog.csdn.net/clever101 早上发现部门的分布式编译服务的服务端崩溃了,原来是IncrediBuild Coordinator服务启动不了.启动该 ...
- Lucene学习总结之六:Lucene打分公式的数学推导 2014-06-25 14:20 384人阅读 评论(0) 收藏
在进行Lucene的搜索过程解析之前,有必要单独的一张把Lucene score公式的推导,各部分的意义阐述一下.因为Lucene的搜索过程,很重要的一个步骤就是逐步的计算各部分的分数. Lucene ...
- 【搜索引擎Jediael开发4】V0.01完整代码 分类: H_HISTORY 2014-05-21 21:35 470人阅读 评论(0) 收藏
截止目前,已完成如下功能: 1.指定某个地址,使用HttpClient下载该网页至本地文件 2.使用HtmlParser解释第1步下载的网页,抽取其中包含的链接信息 3.下载第2步的所有链接指向的网页 ...
- js进阶 11-17 juqery如何查找一个元素的同级元素
js进阶 11-17 juqery如何查找一个元素的同级元素 一.总结 一句话总结:三个方法,向前(prev()),向后(next())和兄弟(siblings()),而前面两个每个都对应三个,pre ...
- Android多线程研究(1)——线程基础及源码剖析
从今天起我们来看一下Android中的多线程的知识,Android入门容易,但是要完成一个完善的产品却不容易,让我们从线程开始一步步深入Android内部. 一.线程基础回顾 package com. ...
- c# 调用ArcEngine的GP工具
转自原文c# 调用ArcEngine的GP工具,AE调用GP工具 IAoInitialize m_AoInitialize = new AoInitializeClass(); esriLicense ...
- Django之分页显示文章
1.项目:http://www.cnblogs.com/jasonhaven/p/7493422.html 2.任务描述:页面分页显示文章 3.源代码 后台: from django.core.pag ...