C#编程总结(十二)断点续传
C#编程总结(十二)断点续传
我们经常使用下载工具,如bit精灵、迅雷、FlashGet,这些软件都支持断点续传。
断点续传即下载任务暂停后可以继续,而无需重新下载,即下载时需要通知服务器的起始位置。如果允许多线程进行分片下载,必须提供起始-截止位置。说到底就是可以选择下载某个片段,整个文件的字节流,可以截取流的片段,也能实现流的累积,最终完成文件下载。
一、原理
在 HTTP/1.1里新增的一个头属性:Range,也是现在众多号称多线程下载工具(如 FlashGet、迅雷等)实现多线程下载的核心所在。老版本的HTTP协议不支持,所以一些老的服务器还不支持断点续传。
Range(请求参数)
用于请求头中,指定第一个字节的位置和最后一个字节的位置,一般格式:
Range:(unit=first byte pos)-[last byte pos]
例如:Range:100-199,取文件流的100至199之间的字节。
Range:100,取位置为100后的所有字节。如果range 为正值,服务器应该开始发送从指定的 range 参数到 HTTP 实体中数据的末尾之间的数据。
Range:-99,取开始的100个字节。如果range 为负值,服务器应该开始发送从 HTTP 实体中数据的开头到指定的 range 参数之间的数据。
Content-Range (响应参数)
用于响应头,指定整个实体中的一部分的插入位置,他也指示了整个实体的长度。在服务器向客户返回一个部分响应,它必须描述响应覆盖的范围和整个实体长度。
一般格式:
- Content-Range: bytes (unit first byte pos) - [last byte pos]/[entity legth]
例如:Content-Range: bytes 1024000-1126399/7421120
HTTP协议:http://www.w3.org/Protocols/rfc2616/rfc2616.html
二、C#中实现
在C#中使用AddRange方法向请求添加指定范围的字节范围标头
- System.Net.HttpWebRequest
所有的方法:
常用的方法为例:
- void AddRange(long from, long to);
指定范围起始、终止位置,来请求该片段的数据。
- // 摘要:
- // 向请求添加指定范围的字节范围标头。
- //
- // 参数:
- // from:
- // 开始发送数据的位置。
- //
- // to:
- // 停止发送数据的位置。
- public void AddRange(long from, long to);
我们通过该方法,基于HTTP协议实现了断点续传,支持暂停、继续下载功能,为了更清晰显示效果,提供了进度条显示。效果图:
Request
请求的Range参数,我们可以清晰看到起具体值,这就是请求的片段。在这里可以清晰的看到HTTP协议版本、请求方法、请求地址等信息
Response
服务器根据请求的Range参数,只返回该片段的数据。我们可以清晰看到Content-Range的具体值。
注意:返回的StatusCode变为了PartialContent,说明是部分数据。
代码 含义
200 OK 请求成功返回
206 Partial Content 部分数据
下载的核心代码:
- /// <summary>
- /// 下载
- /// </summary>
- public void Download()
- {
- //从0计数,需要减一
- long from = this.currentSize;
- if (from < )
- {
- from = ;
- }
- long to = this.currentSize + this.step - ;
- if (to >= this.totalSize && this.totalSize > )
- {
- to = this.totalSize - ;
- }
- this.Download(from, to);
- }
- /// <summary>
- /// 下载
- /// </summary>
- /// <param name="url"></param>
- /// <param name="range"></param>
- public void Download(long from,long to)
- {
- if (this.totalSize == )
- {
- GetTotalSize();
- }
- if (from >= this.totalSize || this.currentSize >= this.totalSize)
- {
- this.isFinished = true;
- return;
- }
- HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
- //request.Method = "GET";
- request.AddRange("bytes", from, to);
- HttpWebResponse response = (HttpWebResponse)request.GetResponse();
- string result = string.Empty;
- if (response != null)
- {
- byte[] buffer = this.Buffer;
- using (Stream stream = response.GetResponseStream())
- {
- int readTotalSize = ;
- int size = stream.Read(buffer, , buffer.Length);
- while (size > )
- {
- //只将读出的字节写入文件
- fs.Write(buffer, , size);
- readTotalSize += size;
- size = stream.Read(buffer, , buffer.Length);
- }
- //更新当前进度
- this.currentSize += readTotalSize;
- //如果返回的response头中Content-Range值为空,说明服务器不支持Range属性,不支持断点续传,返回的是所有数据
- if (response.Headers["Content-Range"] == null)
- {
- this.isFinished = true;
- }
- }
- }
- }
项目源码:
http://files.cnblogs.com/yank/DownloadSample.rar
三、多线程下载
上述例子提供了简单的断点续传功能,如果想再进一步实现多线程下载。原理很简单,我们只需根据所要下载的文件大小,进行分块,没块启动一个线程进行下载,线程只负责下载自己负责的片段,一定要严格设置Range的值。具体实现这里不再介绍,如有兴趣,下来可以继续研究。
提醒一下:多线程下载的字节流如何保存为文件,是否必须按照先后顺序?
C#编程总结(十二)断点续传的更多相关文章
- 学习ASP.NET Core Razor 编程系列十二——在页面中增加校验
学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...
- 通过Socket实现TCP编程(十二)
原文链接:https://www.cnblogs.com/hysum/p/7531529.html Socket通信 : TCP协议是面向对象连接.可靠的.有序的,以字节流的方式发送数据. 基于TCP ...
- 【读书笔记】C#高级编程 第二十二章 安全性
(一)身份验证和授权 安全性的两个基本支柱是身份验证和授权.身份验证是标识用户的过程,授权在验证了所标识用户是否可以访问特性资源之后进行的. 1.标识和Principal 使用标识可以验证运行应用程序 ...
- 并发编程(十二)—— Java 线程池 实现原理与源码深度解析 之 submit 方法 (二)
在上一篇<并发编程(十一)—— Java 线程池 实现原理与源码深度解析(一)>中提到了线程池ThreadPoolExecutor的原理以及它的execute方法.这篇文章是接着上一篇文章 ...
- Linux网络编程学习(十二) ----- 结语
该书提前看完了,重点看了第四章和第六章,第七章以后只是大致浏览了一下,如果以后工作中涉及这一块再仔细研究一下,大概花了二十天的样子,主要了解了进程间的通信方式.socket编程以及五种I/O模式,看的 ...
- Java并发编程(十二)Callable、Future和FutureTask
一.Callable与Runnable 先说一下java.lang.Runnable吧,它是一个接口,在它里面只声明了一个run()方法: public interface Runnable { pu ...
- 【读书笔记】C#高级编程 第十二章 动态语言扩展
(一)DLR C#4的动态功能是Dynamic Language Runtime(动态语言运行时,DLR)的一部分.DLR是添加到CLR的一系列服务. (二)dynamic类型 dynamic类型允许 ...
- java 面向对象编程 --第十二章 JDK常用类
1. 系统类 java.lang包 System类 sys.out;sys.exit;sys.gc; sys.currentTimeMillis();----得到从1970-01-01到当前时间 ...
- Java并发编程(十二)-- 阻塞队列
在介绍Java的阻塞队列之前,我们简单介绍一下队列. 队列 队列是一种数据结构.它有两个基本操作:在队列尾部加人一个元素,和从队列头部移除一个元素就是说,队列以一种先进先出的方式管理数据,如果你试图向 ...
- python系统编程(十二)
异步 同步调用就是你 喊 你朋友吃饭 ,你朋友在忙 ,你就一直在那等,等你朋友忙完了 ,你们一起去 异步调用就是你 喊 你朋友吃饭 ,你朋友说知道了 ,待会忙完去找你 ,你就去做别的了. from m ...
随机推荐
- C#Light(包括unity一切C#环境可用嵌入式脚本)0.10B稳定版发布,功能已定型
yo,如题,这个呕心沥血的脚本语言终于完成了. 后面我会: 1.逐渐做一些例子.说明 2.逐渐测试我能获取到的dotnet环境,保证在所有的平台都能正常执行 3.积极推广 0.10B版本较之前的区别主 ...
- 两个Fragment之间如何传递数据
FragmentA启动FragmentB,做一些选择操作后,返回FragmentA,需要把FragmentB里面选择的数据传回来.有什么办法? Fragment之间不能直接通信,必须通过Activit ...
- VS 2008 生成操作中各个选项的差别
近日,在编译C#项目时经常发现有些时候明明代码没错,但就是编译不过,只有选择重新编译或者清理再编译才会不出错,本着求学的态度,搜罗了下VS2008IDE中生成操作的种类以及差别,整理如下: 内容( ...
- Redis学习笔记~分布式的Pub/Sub模式
回到目录 redis的客户端有很多,这次用它的pub/sub发布与订阅我选择了StackExchange.Redis,发布与订阅大家应该很清楚了,首先一个订阅者,订阅一个服务,服务执行一些处理程序(可 ...
- Bootstrap~多级导航(级联导航)的实现
回到目录 在bootstrap官方来说,导航最多就是两级,两级以上是无法实现的,大叔找了一些第三方的资料,终于找到一个不错的插件,使用上和效果上都还不错,现在和大家分享一下 插件地址:http://v ...
- EF架构~在global.asax里写了一个异常跳转,不错!
回到目录 一般地,网站出现异常后,我们会通过设置web.config的方法来实现友好页的显示,这个方法比较常用,但捕捉的信息不是很具体,在程序测试阶段,我们可以通过global.asax来实现友好的, ...
- JS util
一.返回上一页(history) 发觉有两种用法: 1.javascript:history.back(-1); 2.javascript:history.go(-1); 它们俩的区别是: histo ...
- mybatis插入的同时获取主键id
<insert id="insertAndReturnId" parameterType="com.qianlong.cms.entity.AppCmsRole&q ...
- 简述移动端IM开发的那些坑:架构设计、通信协议和客户端
1.前言 有过移动端开发经历的开发者都深有体会:移动端IM的开发,与传统PC端IM有很大的不同,尤其无线网络的不可靠性.移动端硬件设备资源的有限性等问题,导致一个完整的移动端IM架构设计和实现都充满着 ...
- Java 线程 — ThreadLocal
ThreadLocal 先来看看ThreadLocal的注释: This class provides** thread-local variables**. These variables diff ...