NodeJS使用node-fetch下载文件并显示下载进度
# 最简单的下载文件
适用于小文件下载
const fetch = require("node-fetch");
const fs = require("fs");
fetch("文件url", {
method: 'GET',
headers: { 'Content-Type': 'application/octet-stream' },
}).then(res => res.buffer()).then(_=>{
fs.writeFile("文件保存路径", _, "binary", function (err) {
if (err) console.error(err);
else console.log("下载成功");
});
})
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 下载较大的文件
const fetch = require("node-fetch");
const fs = require("fs");
const path = require("path");
const progressStream = require('progress-stream');
//下载 的文件 地址
let fileURL = "https://nodejs.org/dist/v12.18.3/node-v12.18.3-x64.msi";
//下载保存的文件路径
let fileSavePath = path.join(__dirname, path.basename(fileURL));
//缓存文件路径
let tmpFileSavePath = fileSavePath + ".tmp";
//创建写入流
const fileStream = fs.createWriteStream(tmpFileSavePath).on('error', function (e) {
console.error('error==>', e)
}).on('ready', function () {
console.log("开始下载:", fileURL);
}).on('finish', function () {
//下载完成后重命名文件
fs.renameSync(tmpFileSavePath, fileSavePath);
console.log('文件下载完成:', fileSavePath);
});
//请求文件
fetch(fileURL, {
method: 'GET',
headers: { 'Content-Type': 'application/octet-stream' },
// timeout: 100,
}).then(res => {
//获取请求头中的文件大小数据
let fsize = res.headers.get("content-length");
//创建进度
let str = progressStream({
length: fsize,
time: 100 /* ms */
});
// 下载进度
str.on('progress', function (progressData) {
//不换行输出
let percentage = Math.round(progressData.percentage) + '%';
console.log(percentage);
// process.stdout.write('\033[2J'+);
// console.log(progress);
/*
{
percentage: 9.05,
transferred: 949624,
length: 10485760,
remaining: 9536136,
eta: 42,
runtime: 3,
delta: 295396,
speed: 949624
}
*/
});
res.body.pipe(str).pipe(fileStream);
}).catch(e => {
//自定义异常处理
console.log(e);
});
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
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
# 断点续传
这里我根据原理将下载代码进行修改
const fetch = require("node-fetch");
const fs = require("fs");
const path = require("path");
const progressStream = require('progress-stream');
//下载 的文件 地址
let fileURL = "https://npm.taobao.org/mirrors/node/v14.8.0/node-v14.8.0-x64.msi";
//下载保存的文件路径
let fileSavePath = path.join(__dirname, path.basename(fileURL));
//缓存文件路径
let tmpFileSavePath = fileSavePath + ".tmp";
//下载进度信息保存文件
let cfgFileSavePath = fileSavePath + ".cfg.json";
let downCfg = {
rh: {},//请求头
percentage: 0,//进度
transferred: 0,//已完成
length: 0,//文件大小
remaining: 0,//剩余
first: true//首次下载
};
let tmpFileStat = { size: 0 };
//判断文件缓存 与 进度信息文件是否存在
if (fs.existsSync(tmpFileSavePath) && fs.existsSync(cfgFileSavePath)) {
tmpFileStat = fs.statSync(tmpFileSavePath);
downCfg = JSON.parse(fs.readFileSync(cfgFileSavePath, 'utf-8').trim());
downCfg.first = false;
//设置文件
downCfg.transferred = tmpFileStat.size;
}
//创建写入流
let writeStream = null;
//请求头
let fetchHeaders = {
'Content-Type': 'application/octet-stream',
"Cache-Control": "no-cache",
Connection: "keep-alive",
Pragma: "no-cache",
};
//追加请求范围
if (downCfg.length != 0) {
fetchHeaders.Range = "bytes=" + downCfg.transferred + "-" + downCfg.length;//71777113
}
if (downCfg.rh["last-modified"]) {
fetchHeaders["last-modified"] = downCfg.rh["last-modified"];
}
//校验文件头
const checkHerder = [
"last-modified",//文件最后修改时间
"server",//服务器
// "content-length",//文件大小
"content-type",//返回类型
"etag",//文件标识
];
fetch(fileURL, {
method: 'GET',
headers: fetchHeaders,
// timeout: 100,
}).then(res => {
let h = {};
res.headers.forEach(function (v, i, a) { h[i.toLowerCase()] = v; });
// console.log(h);
//文件是否发生变化
let fileIsChange = false;
//是否首次下载
if (downCfg.first) {
//记录相关信息
for (let k of checkHerder) downCfg.rh[k] = h[k];
downCfg.length = h["content-length"];
} else {
//比较响应变化
for (let k of checkHerder) {
if (downCfg.rh[k] != h[k]) {
fileIsChange = true;
break;
}
}
//是否运行范围下载
downCfg.range = res.headers.get("content-range") ? true : false;
}
//创建文件写入流
writeStream = fs.createWriteStream(tmpFileSavePath, { 'flags': !downCfg.range || fileIsChange ? 'w' : 'a' })
.on('error', function (e) {
console.error('error==>', e)
}).on('ready', function () {
console.log("开始下载:", fileURL);
}).on('finish', function () {
//下载完成后重命名文件
fs.renameSync(tmpFileSavePath, fileSavePath);
fs.unlinkSync(cfgFileSavePath);
console.log('文件下载完成:', fileSavePath);
});
//写入信息文件
fs.writeFileSync(cfgFileSavePath, JSON.stringify(downCfg));
//获取请求头中的文件大小数据
let fsize = h["content-length"];
//创建进度
let str = progressStream({
length: fsize,
time: 200 /* ms */
});
//创建进度对象
str.on('progress', function (progressData) {
//不换行输出
let percentage = Math.round(progressData.percentage) + '%';
console.log(percentage);
// console.log(`
// 进度 ${progressData.percentage}
// 已完成 ${progressData.transferred}
// 文件大小 ${progressData.length}
// 剩余 ${progressData.remaining}
// ${progressData.eta}
// 运行时 ${progressData.runtime}
// ${ progressData.delta}
// 速度 ${ progressData.speed}
// `);
// console.log(progress);
/*
{
percentage: 9.05,
transferred: 949624,
length: 10485760,
remaining: 9536136,
eta: 42,
runtime: 3,
delta: 295396,
speed: 949624
}
*/
});
res.body.pipe(str).pipe(writeStream);
res.headers.forEach(function (v, i, a) {
console.log(i + " : " + v);
})
}).catch(e => {
//自定义异常处理
console.log(e);
});
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
上次更新: 2024/01/30, 00:35:17
- 02
- Node与GLIBC_2.27不兼容解决方案08-19
- 03
- Git清空本地文件跟踪缓存08-13