# 文件下载
# 带进度条下载
本地启动服务器,且准备下载文件 video.mp4
const express = require('express');
const app = express();
const path = require("path");
const fs = require("fs");
app.listen('8888', () => console.log('listen 8888'));
const bdParser = require('body-parser');
app.use(bdParser.urlencoded({ extended: true }));
app.use(bdParser.json());
// 允许跨域中间件
const allowCors = function (req, res, next) {
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type');
res.header('Access-Control-Allow-Credentials', 'true');
next();
};
app.use(allowCors); // 使用跨域中间件
//
const file = fs.readFileSync(path.join(__dirname, "./video.mp4"));
app.get("/size", (req, res) => {
const size = file.length;
res.send({ size: file.length, type: "video/mp4" })
})
app.get("/file", (req, res) => {
res.send(file);
});
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
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
<body>
<button id="downloadBtn1">下载</button>
<input type="range" value="0" id="process1" disabled>
<button id="downloadBtn2">下载</button>
<input type="range" value="0" id="process2" disabled>
<script>
const listener = (btn, processEl) => {
let totleSize;
const loopLoad = async (reader) => {
let len = 0;
const chunks = [];
// 重点:轮询拿到 chunks 返回 Uint8Array 数据
while (true) {
const { value, done } = await reader.read();
if (done) {
const u8Arr = new Uint8Array(len);
chunks.reduce((offset, chunk) => {
u8Arr.set(chunk, offset);
return offset + chunk.length;
}, 0);
processEl.value = 100;
return u8Arr;
}
chunks.push(value);
len += value.length;
processEl.value = Math.floor(len / totleSize * 100);
}
}
const downloadData = async () => {
const { size, type } = await fetch("http://127.0.0.1:8888/size").then(res => res.json());
totleSize = size;
const response = await fetch("http://127.0.0.1:8888/file");
// 重点:读取进度,但是body只能读取一次,所以不能执行 res.json
const reader = response.body.getReader();
const unit8Array = await loopLoad(reader); // 直接拿到 u8Array 类型
const blob = new Blob([unit8Array], { type });
//
const a = document.createElement("a");
a.download = `p${fileNameMap[type]}`;
a.href = window.URL.createObjectURL(blob); // 将缓存转化成 url
a.click();
};
btn.addEventListener("click", downloadData);
return downloadData;
}
listener(downloadBtn1, process1);
listener(downloadBtn2, process2);
</script>
<script>
// 文件类型和尾缀 map
const fileNameMap = {
"video/mp4": ".mp4",
};
</script>
</body>
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
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