🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
## **断点续传** 什么是断点续传? 答:从那个点点断开继续传。。。 额,看起来也没毛病... 再问一遍,什么是断点续传? 答: > 断点续传就是从文件上次中断的地方开始重新下载或上传,当下载或上传文件的时候,如果没有实现断点续传功能,那么每次出现异常或者用户主动的暂停,都会去重头下载,这样很浪费时间。所以断点续传的功能就应运而生了。要实现断点续传的功能,需要客户端记录下当前的下载或上传进度,并在需要续传的时候通知服务端本次需要下载或上传的内容片段。 > 说起到断点续传,他的关键点是什么? * 如何告知服务器,从指定的位置下载 * 如何知道客户端想要的指定位置是多少 其实,很简单,并不需要我们去写一些什么东西。HTTP协议本身就支持断点续传了。只要我们通过方式告诉服务器,从制定位置开始下载就可以了。 来来来,http协议搞起来~~~~~~ 先来提问一下昂,说到http想到了什么??? (在线投骰子?还是谁能**~~自告奋勇~~**???) ``` /** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * **/ ``` ## **Range & Content-Length & Content-Range & If-Range** 这些都是 HTTP 包中 Header 头部的一些字段信息,其中 Range 和 If-Range 是请求头中的字段,Content-Length 和 Content-Range 是响应头中的字段。 ### **Range** 当请求头中出现 Range 字段时,表示告知服务端,客户端下载该文件想要从指定的位置开始下载,至于 Range 字段属性值的格式有以下几种: | 格式 | 含义 | | --- | --- | | Range:bytes=0-500 | 表示下载从0到500字节的文件,即头500个字节 | | Range:bytes=501-1000 | 表示下载从500到1000这部分的文件,单位字节 | | Range:bytes=-500 | 表示下载最后的500个字节 | | Range:bytes=500- | 表示下载从500开始到文件结束这部分的内容 | 当 app 想实现缩短大文件的下载耗时,可以开启多个下载线程,每个线程只负责文件的一部分下载,当所有线程下载结束后,将每个线程下载的文件按顺序拼接成一个完整的文件,这样就可以达到缩短下载大文件的耗时目的了。 那么就可以使用`Range:bytes=501-1000`这种格式了,每个线程在各自的请求头字段中,以这种格式加入相对应的信息即可达到目的了。 ***** 如果 app 想实现断点续传,文件下载到一半被迫中断,下次启动还可以继续接着上次进度下载时,那么此时可以使用 `Range:bytes=500-` 这种格式了,只要先获取本地那份文件目前的大小,通过在请求头中加入 Range 字段信息即可。 #### **Content-Length** Content-Length 字段出现在响应头中,用于告知客户端此次下载的文件大小。 客户端需要实现下载进度实时更新时,需要知道文件的总大小和目前下载的大小。 想要知道文件总大小? Content-Length中的字段就好 如果想要实现多线程同时分段下载大文件时,在下载前会发送一个不需要携带body信息请求,用于先获取响应头中的Content-Length字段 > * 如果这条链接是一次性将整个文件下载下来的,那么 Content-Length 就表示这个文件的总大小。 > * 如果这条链接指定了 Range,表明了只是下载文件的指定部分的内容,那么此时 Content-Length 表示的就只是这一部分的大小。 所以,如果客户端实现了下载进度实时更新功能时,需要注意一下。因为如果文件是断点续传的,那么进度条的分母就不能用每次 HTTP 链接中的 Content-Length。要么下载前先发一条获取用于文件总大小的请求,然后一直维护着这个数据,要么就使用 Content-Range 字段。 ***** #### **Content-Range** Content-Range 字段也是出现在响应头中,用于告知客户端此链接下载的文件是哪个部分的,以及文件的总大小。 比如,当客户端在请求头中指定了`Range:bayes=501-1000`来下载一个总大小为 2000 字节文件的中间一部分内容时,此时,响应头中的 Content-Range 字段信息如下: `Content-Range:bytes 501-1000/2000` 斜杠前表示此链接下载的文件是哪一部分,斜杠后表示文件的总大小。 ***** #### **If-Range** 断点续传,说白点也就是分多次下载,既然不是一次性下载,那么就无法保证多次下载的间隔。 也就是说,有可能出现这种场景: >这次由于某些原因只下载的一部分,而下次重启继续下载,但可能等到过了很多天后才重启去继续下载,如果在这期间,服务端的这份文件更新了怎么办? 只要不是一次性下载的,那么就有可能会出现这种场景,显然,这时候,就不希望断点续传了,而是要让客户端直接**重头开始下载**,毕竟文件都已经发生更新了,不是同一份了,再继续恢复下载也没有什么意义。 **客户端要如何知道服务端的文件是否发生变化,要重头下载呢?** 这时就可以结合 If-Range 字段来实现了,这个也是在请求头中的字段,跟 Range 字段一起使用,它的作用是给 Range 字段生效设置了一些条件,只有满足这些条件,Range 才能生效。 也就是说,只有先满足 If-Range,那么才能通过 Range 来实现断点续传。 那它的条件值可以设置为哪些呢?有两种,Last-Modified 或者 ETag,这两个也都是响应头中的字段。**但不能将两者同时使用。** (if-Range)参考链接:[https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/If-Range](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/If-Range) ***** #### **抓包:** > 1\. https://www.taobao.com/sw.js > 2. https://www.baidu.com/img/bd\_logo1.png?where=super > 3.https://mat1.gtimg.com/pingjs/ext2020/qqindex2018/dist/img/qq\_logo\_2x.png 4.https://www.magicdatatech.com/filespath/files/20191104064048.png 鲨鱼软件的抓包示例: ![](https://img.kancloud.cn/1e/04/1e0410999563f7e8b28e30f4bca3b3e9_416x366.png) 首先先发起一个请求,设置了不携带 BODY 信息,这样就可以在下载前先获取到文件的总大小。 ![](https://img.kancloud.cn/0f/99/0f990ce339c674d17136aefaf79d950e_506x481.png) 这是下载中断后,重启想要继续下载时发起的请求信息,请求头中指定了 `Range:bytes=12341380-` 表示本地已经下载了这么多,需要从这里开始继续往下下载。 响应头中返回了这部分的内容,并在 Content-Length 和 Content-Range 字段中给出了相关信息。 ``` // 创建一个XMLHttpRequest对象 // IE6, IE5 // var xhr = new ActiveXObject("Microsoft.XMLHTTP"); // IE7+, Firefox, Chrome, Opera, Safari var xhr = new XMLHttpRequest() // 重置服务器端返回的类型 xhr.overrideMimeType('image/png') // 初始化一个请求 参数3:表示该请求应该以`异步模式`执行。 xhr.open('GET', '[https://imgcps.jd.com/ling/7641991/5bmz5p2\_55S16KeG5beo5YiS566X/5L2g5YC85b6X5YWl5omL/p-5bd8253082acdd181d02f9d8/e129873b/590x470.jpg](https://imgcps.jd.com/ling/7641991/5bmz5p2_55S16KeG5beo5YiS566X/5L2g5YC85b6X5YWl5omL/p-5bd8253082acdd181d02f9d8/e129873b/590x470.jpg)', true) // 超时时间,单位是毫秒 xhr.timeout = 2000; // 设置 HTTP 请求头的值。在`open()`之后、`send()`之前调用`setRequestHeader()`方法。 xhr.setRequestHeader('range', 'bytes=0-10') // 发送请求。如果请求是异步的(默认),那么该方法将在请求发送后立即返回。 xhr.send() // 只要 `readyState` 属性发生变化,就会调用相应的处理函数。这个回调函数会被用户线程所调用。当一个`XMLHttpRequest`请求被abort()方法取消时,其对应的 `readystatechange`事件不会被触发。 xhr.onreadystatechange = function(){ if ( xhr.readyState == 4 && xhr.status == 206 ) { console.info(xhr) console.info( xhr.response); } else { console.info( xhr.statusText ); } } ``` ### **js 中的 Blob对象** https://www.jianshu.com/p/b322c2d5d778