HTTP流式分块上传是指将大文件分成多个小块(chunk)进行传输的技术,这种技术可以:
async function uploadFileInChunks(file, uploadUrl, chunkSize = 1024 * 1024) {
const fileSize = file.size;
let start = 0;
let chunkIndex = 0;
while (start < fileSize) {
const end = Math.min(start + chunkSize, fileSize);
const chunk = file.slice(start, end);
const formData = new FormData();
formData.append('file', chunk);
formData.append('chunkIndex', chunkIndex);
formData.append('totalChunks', Math.ceil(fileSize / chunkSize));
formData.append('originalFilename', file.name);
try {
await fetch(uploadUrl, {
method: 'POST',
body: formData,
headers: {
'X-Chunk-Index': chunkIndex,
'X-Total-Chunks': Math.ceil(fileSize / chunkSize),
'X-File-Name': encodeURIComponent(file.name)
}
});
start = end;
chunkIndex++;
} catch (error) {
console.error('上传失败:', error);
throw error;
}
}
// 通知服务器所有分块已上传
await fetch(`${uploadUrl}/complete`, {
method: 'POST',
body: JSON.stringify({
filename: file.name,
totalChunks: Math.ceil(fileSize / chunkSize)
}),
headers: {
'Content-Type': 'application/json'
}
});
}
const express = require('express');
const fs = require('fs');
const path = require('path');
const multer = require('multer');
const app = express();
// 临时存储分块
const storage = multer.diskStorage({
destination: (req, file, cb) => {
const tempDir = path.join(__dirname, 'temp');
if (!fs.existsSync(tempDir)) {
fs.mkdirSync(tempDir, { recursive: true });
}
cb(null, tempDir);
},
filename: (req, file, cb) => {
const filename = req.headers['x-file-name'] || file.originalname;
const chunkIndex = req.headers['x-chunk-index'];
cb(null, `${filename}.part${chunkIndex}`);
}
});
const upload = multer({ storage });
app.post('/upload', upload.single('file'), (req, res) => {
res.status(200).json({ message: '分块上传成功' });
});
app.post('/upload/complete', express.json(), (req, res) => {
const { filename, totalChunks } = req.body;
const tempDir = path.join(__dirname, 'temp');
const outputPath = path.join(__dirname, 'uploads', filename);
// 确保上传目录存在
if (!fs.existsSync(path.dirname(outputPath))) {
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
}
// 合并所有分块
const writeStream = fs.createWriteStream(outputPath);
for (let i = 0; i < totalChunks; i++) {
const chunkPath = path.join(tempDir, `${filename}.part${i}`);
const chunkData = fs.readFileSync(chunkPath);
writeStream.write(chunkData);
fs.unlinkSync(chunkPath); // 删除临时分块
}
writeStream.end();
res.status(200).json({ message: '文件合并完成' });
});
app.listen(3000, () => console.log('服务器运行在3000端口'));
这个实现方案可以根据具体需求进行调整,例如添加加密、压缩或进度显示等功能。
没有搜到相关的文章