大檔案分片上傳
代碼示例
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 檔案目錄圖示
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIyVGduV2YfNWawNyZuBnL3kTM4cTMhR2MzQ2MjFmNiVGNkRTO5UTNykTNzMjNjBzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)