是否在因为曾单线程下载速度缓慢而焦虑

http 可以范围请求

  1. Range: <unit>=<range-start>-
  2. Range: <unit>=<range-start>-<range-end>
  3. Range: <unit>=<range-start>-<range-end>, <range-start>-<range-end>
  4. Range: <unit>=<range-start>-<range-end>, <range-start>-<range-end>, <range-start>-<range-end>
  • unit:范围请求所采用的单位,通常是字节(bytes)。
  • :一个整数,表示在特定单位下,范围的起始值。
  • :一个整数,表示在特定单位下,范围的结束值。这个值是可选的,如果不存在,表示此范围一直延伸到文档结束。

    方案

    1. 通过 head 请求获取到文件的长度

    1. xhr.getResponseHeader("Content-Length")

    2. 通过 asyncPool 来控制并发下载任务

    3. 实现一个根据参数发送范围请求的函数

    下载 arrayBuffer 片段
    1. function getBinary (requestUrl, start, end, target) {
    2. return new Promise((rs, rj) => {
    3. try {
    4. let xhr = new XMLHttpRequest()
    5. xhr.open("GET", url, true)
    6. xhr.sendRequestHeader('range', `bytes=${start}-${end}`)
    7. xhr.responseType = 'arraybuffer'
    8. xhr.onload = function () {
    9. }
    10. xhr.send()
    11. } catch (err) {
    12. reject(new Error(err))
    13. }
    14. })
    15. }
    [ArrayBuffer](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) 对象通过表示通用的,固定长度的原始二进制数据缓冲区,我们是不能对它直接操作的。而是要通过 DataView 或者 类型对象来操作,它会将缓冲区的数据表示为特定的格式,我们可以通过这些格式来读取缓冲区内容。

    4. 转化ArrayBuffer[Uint8Array](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) (八位无符号整型数组

    5. 通过 save 函数保存 FileSaver.js

    ```javascript function saveAs({ name, buffers, mime = “application/octet-stream” }) { const blob = new Blob([buffers], { type: mime }); const blobUrl = URL.createObjectURL(blob); const a = document.createElement(“a”); a.download = name || Math.random(); a.href = blobUrl; a.click(); URL.revokeObjectURL(blob); }
  1. <a name="EjMgn"></a>
  2. ##### 5. 最终实现
  3. ```javascript
  4. // 获取到拼接后的 uint8Array
  5. async function download({ url, chunkSize, poolLimit = 1 }) {}
  6. // 下载
  7. function multiThreadedDownload(url) {
  8. if (!url || !/https?/.test(url)) return
  9. console.log("多线程下载开始: " + +new Date())
  10. download({
  11. url,
  12. chunkSize: 0.1 * 1024 * 1024,
  13. poolLimit: 6
  14. }).then((buffers) => {
  15. console.log('多线程下载结束: ' + +new Date())
  16. saveAs({ buffers, name: '我的压缩包', mime: 'application/zip' })
  17. })
  18. }

6. well done

你可能会质疑,下载速度取决于网络,并行明明会只是分摊带宽,为何会加速下载
其实用户端和服务器,都可以想象成两个水管
假设 服务器速度 a, 用户速度 b
单线程的时候
哪个速度小用哪个
多线程
a > b 用户带宽相当于受限,所以 这个时候多线程 只是起到分摊速度的效果
b > a 此时可以多线程加速下载
一般服务器会限制耽搁宽带的下载速度,往往是通过限制单个TCP下载,这种情况下,开启多线程是可以增加下载速度的,但有的也会限制单个IP的最大速度。

HTTP/1.1 同一站点只能并发六个请求,多余的会放到下一批次,但是 HTTP/2.0 不受这个限制,多路复用替代了 HTTP/1.1 的序列和阻塞机制

接着就可以愉快的下载了!