先介绍文件上传背景
例如:某某Ai产品,需要涉及到用户的自定义模型(1G以上)
可能会遇到的问题:
- 网络断开之后,之前传的没了
- 传着传着,网络波动,结果啥都没了
- 关机了,想接着传,做不到
专业术语:
- 断点续传
- 断开重传
- 切片上传
方案:
-
分片上传(Chunk Upload):
-
将大文件切成多个小块(chunk),分别上传到服务器。
-
服务端负责将这些小块重新组装成完整的文件。
-
常用工具:
FileReader
API、Blob
API 和FormData
。 -
示例:
function uploadFileInChunks(file) { const chunkSize = 5 * 1024 * 1024; // 每个块大小为5MB let currentChunk = 0; const totalChunks = Math.ceil(file.size / chunkSize); const uploadChunk = () => { const start = currentChunk * chunkSize; const end = Math.min(file.size, start + chunkSize); const chunk = file.slice(start, end); const formData = new FormData(); formData.append('fileChunk', chunk); formData.append('chunkIndex', currentChunk); fetch('/upload', { method: 'POST', body: formData }).then(response => { if (response.ok) { currentChunk++; if (currentChunk < totalChunks) { uploadChunk(); // 继续上传下一个块 } else { console.log('文件上传完成'); } } }); }; uploadChunk(); }
-
-
断点续传:
- 如果上传过程中断,可以从上次上传到的位置继续上传,而不需要重新上传整个文件。
- 需要服务端支持记录已上传的部分。
-
后台上传(Background Upload):
- 使用
Web Workers
或Service Workers
实现后台上传,用户关闭页面时仍能继续上传。
- 使用
-
利用第三方库:
axios
:可以用axios
实现分片上传和断点续传。FineUploader
、Resumable.js
:这些库封装了大文件上传的逻辑,支持分片上传、断点续传、失败重试等功能。
使用axios示例:
使用 axios
实现分片上传和断点续传的步骤可以分为以下几个部分:
- 分片处理文件
- 记录上传进度
- 断点续传
实现步骤:
1. 分片处理文件
使用 JavaScript 的 Blob.slice()
方法将大文件分成若干小块。然后使用 axios
发送每个块。
2. 记录上传进度
为每个分片上传记录其进度和状态,服务器可以返回已上传的块号或偏移量,客户端根据此信息决定从哪里开始上传。
3. 断点续传
当网络中断时,可以从断点继续上传。需要与服务器协作,服务器端保存上传状态,客户端通过请求确认哪些块已经上传成功,从而跳过这些块。
示例代码:
import axios from 'axios';
// 分片上传的大小(5MB)
const CHUNK_SIZE = 5 * 1024 * 1024;
// 分片上传函数
async function uploadFileInChunks(file) {
const totalChunks = Math.ceil(file.size / CHUNK_SIZE); // 总块数
// 获取已上传的块信息(用于断点续传)
const uploadedChunks = await getUploadedChunks(file.name);
for (let currentChunk = 0; currentChunk < totalChunks; currentChunk++) {
if (uploadedChunks.includes(currentChunk)) {
// 如果该块已上传,跳过
console.log(`Chunk ${currentChunk} already uploaded`);
continue;
}
// 切割文件为块
const start = currentChunk * CHUNK_SIZE;
const end = Math.min(file.size, start + CHUNK_SIZE);
const chunk = file.slice(start, end);
// 创建FormData对象,上传分片
const formData = new FormData();
formData.append('fileChunk', chunk);
formData.append('chunkIndex', currentChunk);
formData.append('fileName', file.name);
try {
// 通过 axios 发送POST请求上传分片
await axios.post('/upload', formData, {
onUploadProgress: (progressEvent) => {
const percentCompleted = Math.round(
(progressEvent.loaded * 100) / progressEvent.total
);
console.log(`Uploading chunk ${currentChunk}: ${percentCompleted}%`);
}
});
console.log(`Chunk ${currentChunk} uploaded successfully`);
} catch (error) {
console.error(`Failed to upload chunk ${currentChunk}`, error);
break;
}
}
console.log('File upload complete');
}
// 获取已上传块信息(用于断点续传)
async function getUploadedChunks(fileName) {
try {
const response = await axios.get(`/upload/status?fileName=${fileName}`);
return response.data.uploadedChunks || [];
} catch (error) {
console.error('Error fetching uploaded chunks', error);
return [];
}
}
- 分片处理:
- 使用
Blob.slice()
将文件按块大小切分。 - 根据文件大小和块大小计算总块数,并逐块上传。
- 使用
- 上传进度:
axios
的onUploadProgress
事件用于监控上传进度,可以显示上传的百分比。
- 断点续传:
- 客户端发送请求到服务器,查询已上传的块号,确保只上传未完成的部分。
服务端处理(示例):
服务端需要能接收分片,保存上传状态。以下是一个简单的服务器处理示例:
js复制代码const express = require('express');
const fs = require('fs');
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// 上传分片
app.post('/upload', (req, res) => {
const { fileName, chunkIndex } = req.body;
const fileChunk = req.files.fileChunk;
// 保存分片到服务器
const chunkPath = `./uploads/${fileName}.part${chunkIndex}`;
fileChunk.mv(chunkPath, (err) => {
if (err) {
return res.status(500).send(err);
}
res.send({ message: 'Chunk uploaded' });
});
});
// 获取已上传的块(用于断点续传)
app.get('/upload/status', (req, res) => {
const { fileName } = req.query;
// 获取文件上传目录下所有已上传的块
const uploadedChunks = fs.readdirSync('./uploads')
.filter(file => file.startsWith(fileName))
.map(file => parseInt(file.split('.part')[1]));
res.send({ uploadedChunks });
});
app.listen(3000, () => {
console.log('Server started on http://localhost:3000');
});
- 前端使用
Blob.slice()
切割文件,通过axios
分块上传。 - 上传进度通过
onUploadProgress
监控。 - 断点续传通过查询服务器上已上传的块,跳过已上传部分。