为什么要分片上传
- 因为对于大文件的上传,如果在上传过程出现了超时,就会上传失败。将大文件分为小文件,提高了上传成功率
- 上传大文件时,只有一个请求,将文件分片,可以并发上传多个小文件,大大的提高了上传速度
- 可以实现断点上传(如果因为某种原因暂停了上传。此时上传只需要从中断的地方开始上传就可以了)
前端分片前置知识
Blob对象:存储大的二进制数据对象 file对象:继承blob对象(可通过input的files字段获取) formData:用于创建表单数据,包括文本、文件
前端实现分片上传
确定一个chunkSize(确定好切片的尺寸) 计算出totalChunk 使用slice切割file文件,将file文件上传到服务端 大体代码如下
function uploadFile(file) {
const totalChunks = Math.ceil(file.size / chunkSize);
let cur = 0;
for (let i = 0; i < totalChunks; i++) {
const start = i * chunkSize;
const end = Math.min(start + chunkSize, file.size);
const chunk = file.slice(start, end);
const formData = new FormData();
formData.append("chunk", chunk);
formData.append("filename", file.name);
formData.append("totalChunks", totalChunks);
formData.append("currentChunk", i);
}
}
// 请求传递formdata数据
服务端实现分片上传
使用writeFileSync写入分片文件 merge接口,使用const writeStrem = fs.createWriteStream(需要写入的路径) 利用writeStrem.write(读取分片文件的data) 最后writeStrem.end()关闭可写流
app.post("/upload", (req, res) => {
const form = new multiparty.Form({});
form.parse(req, (err, fields, files) => {
let data = fs.readFileSync(files.chunk[0].path);
let filename = fields.filename[0].split(".")[0];
if (!fs.existsSync(`./temp/${filename}`)) {
fs.mkdirSync(`./temp/${filename}`, { recursive: true });
}
fs.writeFileSync(`./temp/${filename}/${fields.currentChunk[0]}`, data);
res.send({
errno: 0, // 注意:值是数字,不能是字符串
data: {
url: "成功了",
},
});
});
});
app.post("/merge", (req, res) => {
// return;
req.name = req.name
// 定义分片文件夹路径和合并后的文件路径
const folderPath = "./temp"; // 分片文件夹路径
const mergedFilePath = "./public.pdf"; // 合并后的文件路径
// 分片文件目录
const chunkDir = "./temp";
// 合并后的文件路径
// 读取分片文件列表
const chunks = fs.readdirSync(chunkDir);
chunks.forEach((chunkFileName) => {
const chunkFilePath = path.join(chunkDir, chunkFileName);
const data = fs.readFileSync(chunkFilePath);
fs.appendFileSync(mergedFilePath, data);
fs.unlinkSync(chunkFilePath);
});
res.send({ a: 1 });
});