大文件分片上传

191 阅读2分钟

为什么要分片上传

  • 因为对于大文件的上传,如果在上传过程出现了超时,就会上传失败。将大文件分为小文件,提高了上传成功率
  • 上传大文件时,只有一个请求,将文件分片,可以并发上传多个小文件,大大的提高了上传速度
  • 可以实现断点上传(如果因为某种原因暂停了上传。此时上传只需要从中断的地方开始上传就可以了)

前端分片前置知识

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 });
});