天天看點

大檔案切片上傳大檔案分片上傳

大檔案分片上傳

代碼示例

fs-extra

  • 前端:

    利用 Blob.prototype.slice 方法對大檔案進行分片,和數組的slice類似,調用之後可以傳回大檔案中對應的分片檔案。

    這樣我們就可以把大檔案切分成一個個的小片段,利用

    http的可并發性

    ,同時上傳多個分片,這樣從上傳一個大檔案變成了同時上傳多個小檔案,大大的減少了上傳時間。
    • 分片順序
    由于http的可并發性,多個檔案可以同時上傳,這時記錄

    每個分片的順序

    是尤為重要的,否則之後合并的時候會出錯。

    解決:将傳給後端的檔案名按照:檔案的名稱+index+字尾命名

    後端處理:檔案的名稱作為目前分片的整個檔案夾名稱,index作為分片的名稱

    保證了分片的順序

    • 何時合并分片
    index * chunkSize : index目前切片的順序, chunkSize 每片飯店大小 如果這個大小大于目前檔案總大小,說明所有分片已經全部上傳完成,進行合并分片
<template>
    <div>
        <input type="file" ref="btnFile" />
        <button @click="uploadFile(0)">上傳</button>
    </div>
</template>

<script setup>
import { upphoto, uploadSlice, merge } from "@/api/items";
import { ref } from "vue";
const btnFile = ref(null);
const chunkSize = 1024 * 1024;
const uploadFile = (index) => {
    let file = btnFile.value.files[0];
    let fileNameArr = file.name.split(".");
    let fname = fileNameArr[0];
    let fext = fileNameArr[1];
    let start = index * chunkSize;
    if (start > file.size) {
        handlemerge(file.name);
        return;
    }
    let blob = file.slice(start, start + chunkSize);
    let blobName = `${fname}.${index}.${fext}`; // 檔案名:檔案的名稱+index+字尾命名
    let blobFile = new File([blob], blobName);
    let formData = new FormData();
    formData.append("file", blobFile);
    uploadSlice(formData).then((res) => {
        uploadFile(++index);
    });
};
const handlemerge = (name) => {
    merge({ name }).then((res) => {
        console.log(res);
    });
};
</script>
<style  scoped>
</style>
           
  • node端

    利用appendFileSync将分片資料合并,appendFileSync()方法用于将給定資料同步追加到檔案中。如果新檔案不存在,則會建立一個新檔案。

module.exports = (app) => {
    const express = require('express');
    const router = express.Router();
    const multiparty = require('multiparty')
    const fs = require('fs')
    const fse = require('fs-extra')
    const path = require('path')
    const upload_dir = __dirname + '/../../uploadsMultipart/slice'
    router.post('/uploadSlice', (req, res) => {
        const form = new multiparty.Form({ uploadDir: upload_dir })
        form.parse(req)
        form.on('file', async (name, chunk) => {
            console.log(chunk);
            // 存放切片的目錄 
            let chunkDir = `${upload_dir}/${chunk.originalFilename.split('.')[0]}`
            // 是否存在 chunkDir 這個目錄,不存在就建立一個
            if (!fse.existsSync(chunkDir)) {
                await fse.mkdirs(chunkDir)
            }
            // 原檔案名
            let dPath = path.join(chunkDir, chunk.originalFilename.split('.')[1])
            // 移動檔案
            await fse.move(chunk.path, dPath, { overwrite: true })
            res.send('檔案上傳成功')
        })
    })
    router.post('/merge', async (req, res) => {
        let name = req.body.name
        let fname = name.split('.')[0]
        let chunkDir = path.join(upload_dir, fname)
        let chunks = await fse.readdir(chunkDir)
        // 進行排序合并
        chunks.sort((a, b) => a - b).map(chunkPath => {
            fs.appendFileSync(
                path.join(upload_dir, name),
                fs.readFileSync(`${chunkDir}/${chunkPath}`)
            )
        })
        // 删除檔案夾
        fse.removeSync(chunkDir)
        res.send({
            msg: '合并成功',
            url: `http://localhost:9000/uploadsMultipart/slice/${name}`
        })
    })
    app.use('/admin/api', router);
    
}
           
  • uploadsMultipart/slice 檔案目錄圖示
大檔案切片上傳大檔案分片上傳

繼續閱讀