分片上传需求分析:
项目中很多地方需要上传视频,如果视频很大,上传到服务器需要很多时间 ,这个时候体验就会很差。所以需要前端实现分片上传的功能。
代码分析:
html文件:
<input type="file" id="videoFile" accept="video/*" />
<button id="uploadBtn">上传</button>
javascript 文件:
document.getElementById('uploadBtn').addEventListener('click', async () => {
const fileInput = document.getElementById('videoFile');
const file = fileInput.files[0];
if (!file) {
alert('请选择一个文件');
return;
}
const chunkSize = 5 * 1024 * 1024; // 每个分片的大小,例如 5MB
//分析存在几个分片,假如文件是10M ,那就是两个分片。
const totalChunks = Math.ceil(file.size / chunkSize);
//唯一id,这个是必须的,用来区分不同的分片
const fileId = Date.now().toString(); // 生成一个唯一的文件ID
//遍历分片,去分别上传到服务器
for (let chunkNumber = 0; chunkNumber < totalChunks; chunkNumber++) {
const start = chunkNumber * chunkSize;
const end = Math.min(start + chunkSize, file.size);
const chunk = file.slice(start, end);
//处理数据给后端需要的格式
const formData = new FormData();
formData.append('file', chunk);
formData.append('fileId', fileId);
formData.append('chunkNumber', chunkNumber);
formData.append('totalChunks', totalChunks);
formData.append('fileName', file.name);
try {
const response = await fetch('/upload', {
method: 'POST',
body: formData,
});
if (!response.ok) {
throw new Error('上传失败');
}
console.log(`分片 ${chunkNumber + 1}/${totalChunks} 上传成功`);
} catch (error) {
console.error('上传出错:', error);
break;
}
}
console.log('所有分片上传完成');
});
分析一下上面的formData:
更多的数据格式参考文章:点击跳转
服务端处理:
服务器端需要处理每个分片的上传请求,并在所有分片上传完成后将其合并。
接受分片:
服务器端可以使用类似以下的代码来接收分片:
app.post('/upload', (req, res) => {
const { fileId, chunkNumber, totalChunks, fileName } = req.body;
const chunk = req.files.file;
// 保存分片到临时目录
const chunkPath = `./temp/${fileId}-${chunkNumber}`;
fs.writeFileSync(chunkPath, chunk.data);
res.send({ success: true });
});
合并分片:
当所有分片上传完成后,服务器端可以将它们合并成一个完整的文件:
app.post('/merge', (req, res) => {
const { fileId, totalChunks, fileName } = req.body;
const outputPath = `./uploads/${fileName}`;
const writeStream = fs.createWriteStream(outputPath);
for (let i = 0; i < totalChunks; i++) {
const chunkPath = `./temp/${fileId}-${i}`;
const chunk = fs.readFileSync(chunkPath);
writeStream.write(chunk);
fs.unlinkSync(chunkPath); // 删除临时分片
}
writeStream.end();
res.send({ success: true });
});
前端通知服务器合并分片
在所有分片上传完成后,前端可以发送一个请求通知服务器合并分片:
fetch('/merge', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
fileId: fileId,
totalChunks: totalChunks,
fileName: file.name,
}),
});