# 大文件上传
# 前言
- 大文件上传因为某些不可避免的因素(如网络带宽、网络异常等情况),常规方法将一个文件通过blob形式字节流批量上传将会带来严重的性能问题
- 分片上传将会是一个不错的方案,将文件切割成多个块,依次上传,通过标记文件流key,可以和后台进行完美交互,并且可以实现断点续传等
# 常规的文件上传
代码
// 获取文件输入框和上传按钮
const fileInput = document.getElementById('fileInput');
const uploadButton = document.getElementById('uploadButton');
// 添加事件监听器
fileInput.addEventListener('change', (event) => {
const file = event.target.files[0];
// 如果用户选择了一个文件
if (file) {
// 创建一个Blob对象,代表要上传的文件
const blob = new Blob([file], { type: file.type });
// 创建一个FormData对象,将文件和表单数据添加到其中
const formData = new FormData();
formData.append('file', file);
// 创建一个XMLHttpRequest对象,用于发送HTTP请求
const xhr = new XMLHttpRequest();
// 设置请求头,包括文件类型、大小等信息
xhr.open('POST', '/upload', true);
// 设置请求参数,包括表单数据和文件数据
xhr.send(formData);
// 添加上传进度监听器
xhr.upload.addEventListener('progress', (event) => {
if (event.lengthComputable) {
const percentComplete = Math.round((event.loaded / event.total) * 100);
console.log(`上传进度: ${percentComplete.toFixed(2)}%`);
}
});
// 添加上传完成监听器
xhr.addEventListener('load', (event) => {
console.log('文件上传成功');
});
}
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
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
# 大文件断点续传
源码 (opens new window) nodejs创建的一个server端
const express = require('express');
const uploader = require('express-fileupload');
const {extname, resolve} = require('path');
const {
promises: {
writeFile,
appendFile,
},
existsSync,
} = require('fs');
const app = express();
const port = 3000;
app.use('/', express.static('public'));
app.use(express.json());
app.use(express.urlencoded({
urlencoded: true,
}));
app.use(uploader());
app.post('/api/upload', async (req, res) => {
const {name, size, type, offset, hash} = req.body;
const {file} = req.files;
console.log(name, size, type, offset, hash);
const ext = extname(name)
const filename = resolve(__dirname, `./public/${hash}${ext}`);
if (offset > 0) {
if (!existsSync(filename)) {
res.status(400)
.send({
message: '文件不存在',
});
return;
}
await appendFile(filename, file.data);
res.send({
data: 'appended',
});
return;
}
await writeFile(filename, file.data);
res.send({
data: 'created',
});
});
app.listen(port, () => {
console.log('Server is running at:', port);
});
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
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
客户端
const uploader = document.getElementById('uploader');
const output = document.getElementById('output');
const progress = document.getElementById('progress');
function read(file) {
const reader = new FileReader();
return new Promise((resolve, reject) => {
reader.onload = function () {
resolve(reader.result);
}
reader.onerror = reject;
reader.readAsBinaryString(file);
});
}
uploader.addEventListener('change', async (event) => {
const {files} = event.target;
const [file] = files;
if (!file) {
return;
}
uploader.value = null;
const content = await read(file);
const hash = CryptoJS.MD5(content);
const {size, name, type} = file;
progress.max = size;
const chunkSize = 64 * 1024;
let uploaded = 0;
const local = localStorage.getItem(hash);
if (local) {
uploaded = Number(local);
}
const breakpoint = 1500 * 1024;
while (uploaded < size) {
const chunk = file.slice(uploaded, uploaded + chunkSize, type);
const formData = new FormData();
formData.append('name', name);
formData.append('type', type);
formData.append('size', size);
formData.append('file', chunk);
formData.append('hash', hash);
formData.append('offset', uploaded);
try {
await axios.post('/api/upload', formData);
} catch (e) {
output.innerText = '上传失败。' + e.message;
return;
}
uploaded += chunk.size;
localStorage.setItem(hash, uploaded);
progress.value = uploaded;
}
output.innerText = '上传成功。';
});
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
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