做了大文件分块上传才知道有多烦
//用到的模块
const fs = require('fs');
const multer = require('multer');
const path = require('path');
直接上接口代码
router.post('/chunks', chunks_upload.single('file'), async (req, res) => {
// console.log(req.file);
// 检查temp目录是否存在 如果不存在就创建
if (!fs.existsSync(tempDir)) fs.mkdirSync(tempDir, { recursive: true });
// 异常捕获
try {
// 获取文件信息
const chunkIndex = req.body.index; // 当前为第几块
const chunkCount = req.body.count; // 总共有几块
const filename = req.body.filename; // 当前文件的文件名
console.log(req.files);
// 创建一个唯一的临时文件名
const temp_filename = Date.now().toString();
// 取出文件名不包含.扩展名
let name = filename.split('.')[0];
// 根据filename创建一个以temp_filename命名的临时文件夹路径
const temp_dir = path.join(tempDir, name);
// 判断文件夹是否存在 如果不存在就创建
if (!fs.existsSync(temp_dir)) fs.mkdirSync(temp_dir, { recursive: true });
const tempFilePath = path.join(temp_dir, name + '_' + chunkIndex);// 临时文件路径
// 使用文件流处理文件上传
const readStream = fs.createReadStream(req.file.path);// 创建可读流
const writeStream = fs.createWriteStream(tempFilePath);// 创建可写流
// 监听可读流完成事件
readStream.on('end', async () => {
// 关闭写入流
writeStream.end();
// 判断上传的文件是否为最后一块
if (chunkIndex == chunkCount - 1) {
// 如果是最后一块 就将所有的文件块合并成一个文件
// 创建一个以temp_filename命名的文件路径 用来保存最终的文件
const targetFilePath = path.join(__dirname, '../uploads/chunks', temp_filename, filename);
// 判断targetFilePath文件夹是否存在 如果不存在就创建
if (!fs.existsSync(path.dirname(targetFilePath))) fs.mkdirSync(path.dirname(targetFilePath), { recursive: true });
// 创建一个写入流 用来写入最终的文件
const videoWriteStream = fs.createWriteStream(targetFilePath, { flags: 'a' });
// 根据总块数合并文件
for (let i = 0; i < chunkCount; i++) {
// 构建临时文件路径
const chunkPath = path.join(temp_dir, name + '_' + i);
// 读取分块的文件内容
// 文件流方式 解决内存占用过大
// 创建可读流
const readFlow = fs.createReadStream(chunkPath);
readFlow.pipe(videoWriteStream, { end: false }); // 使用pipe来逐块写入
//这里必须要用new Promise((resolve, reject) 不然会无法正确监听事件
await new Promise((resolve, reject) => {
readFlow.on('end', () => {
console.log('读取流结束');
// 删除临时文件
fs.unlinkSync(chunkPath);
resolve();
})
})
// 将读取到的块数据写入到最终的文件中
// 全部读入到内存的方式
// const chunkData = fs.readFileSync(chunkPath);
// // 将读取到的块数据写入到最终的文件中
// videoWriteStream.write(chunkData);
// 全部读入到内存的方式结束
// fs.unlinkSync(chunkPath);
}
// 关闭写入流
console.log('关闭写入流');
videoWriteStream.end();
// 删除临时文件夹
// fs.rmdirSync(temp_dir);
fs.rm(path.join(tempDir, filename), { recursive: true }, (err) => {
if (err) {
console.log('文件删除失败');
console.error(err);
} else {
console.log('文件删除成功');
}
});
fs.rm(path.join(temp_dir), { recursive: true }, (err) => {
if (err) {
console.log('文件夹删除失败');
console.error(err);
} else {
console.log('文件夹删除成功');
}
});
// 返回上传成功的信息
res.send({
code: 200,
msg: '上传成功',
url: `http://localhost:3000/uploads/chunks/${temp_filename}/${filename}`
})
} else {
// 如果不是最后一块 就继续上传
res.send({
code: 200,
msg: '数据块上传成功'
})
}
});
// 将可读流的内容写入到写入流
readStream.pipe(writeStream);// 管道流
} catch (err) {
console.log(err);
res.status(500).send({
code: 500,
msg: '上传失败'
})
}
})