node.js 实现断点下载器

之前写 eva 下载器的时候,就一直想做断点下载,直到最近才实现。

原理

断点下载其实是依赖 http 的 range 参数:

1
res.setHeader("Range", `bytes=${read}-`);

断点下载解决后,还需要添加文件追加

1
res.pipe(fs.createWriteStream(out, { flags: "a" }));

但是并不是所以的资源都支持断点续传,所以要做一下兼容。

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
const EventEmitter = require("events");
const Request = require("request");
const { getRead, getLength, getTotal, startNum } = require("../utils/index");
const fs = require("fs");

class Downloader extends EventEmitter {
constructor() {
super();
this.instance = null;
this.request = this.request.bind(this);
}

/**
* 单例初始化
* @author bubao
* @date 2019-12-30
* @static
* @memberof PromiseRequest
*/
static init() {
if (!this.instance) {
this.instance = new this();
}
return this.instance;
}

/**
* request
* @author bubao
* @date 2019-12-30
* @param {{
* pipe:string, // download path
* hide:boolean, // hiden ora
* time:number, // start time
* size:number, // download size content-length
* }} options { pipe, hiden, time, size, readable, ...opts }
* @returns {Promise}
* @memberof PromiseRequest
*/
request(options) {
const that = this;
const {
pipe, // download path
hiden, // hiden ora
time, // start time
size, // download size content-length
...opts // request options
} = options;
const start = startNum(time);
let read = getRead(options);
let response = 0;
let total = 0;
let speed = 0;

const res = Request(opts);
const Interval = setInterval(() => {
that.emit("progress", {
completed: read,
total,
hiden,
speed,
time: { start },
status: { down: "正在下载。..", end: "完成、n" }
});
speed = 0;
}, 1000);
read && res.setHeader("Range", `bytes=${read}-`);

return new Promise(function(resolve) {
res.on("response", resp => {
const length = resp.headers["content-length"];
response = getLength(
read && length !== undefined
? read + (length - 0)
: length,
size
);
})
.on("data", function(data) {
speed += data.length;
read += data.length;
total = getTotal(
size,
response,
read
);
})
.on("end", () => {
that.emit("progress", {
completed: read,
total: total,
hiden,
speed,
time: { start },
status: { down: "正在下载。..", end: "完成、n" }
});
clearInterval(Interval);
resolve();
});
// 如果 pipe 参数存在,则下载到指定路径
download(res, pipe, read);
});
}
}

/**
* 如果存在 piep 则下载
* @author bubao
* @date 2019-12-30
* @param {buffer} data stream
* @param {string} dir pipe
* @param {boolean} append
*/
function download(data, dir, append) {
if (dir && dir.length) {
const opts = append ? { flags: "a" } : undefined;
data.pipe(fs.createWriteStream(dir || "./", opts));
}
}
module.exports = Downloader;

最后把代码更新到 eva 中,演示如下:

demo


node.js 实现断点下载器
https://bubao.github.io/posts/702b3b5b.html
作者
一念
发布于
2021年2月6日
许可协议