之前看過相關(guān)文章但是一直沒有動手實現(xiàn),這個東西就是為了實現(xiàn)這個而產(chǎn)生的。
前端流程圖
image.png
主要技術(shù)點
切片
- 利用
Blob.prototype.slice
切片 - 獲取切片md5 作為唯一標識
具體代碼
// 計算切片數(shù)量
const page = Math.ceil(file.size / size);
// 初始位置
let start = 0;
// 等待的異步任務(wù)隊列
const promiseList = [];
for (let i = 0; i < page; i++) {
// 計算當前切片的終止位置
const end = start + size;
const item = file.slice(start, end);
// 更新 開始位置
start = end;
// 生成單個塊 (異步)
// const block = generateItemBlock(item, i);
const block = {
key:md5, // md5
index:i, // index
data: item // 切片數(shù)據(jù)
};
}
合并
讀取切片文件獲取文件Buffer
利用Buffer.concat([b1,b2])
合并切片
/**
* 合并切片成文件
* @param {String} filename 文件名
* @param {Array} keys 切片id(臨時文件名)列表
*/
async function mergeFile (filename, keys) {
const readFile = util.promisify(fs.readFile);
// 讀取文件
const files = keys.map(key => {
return readFile(path.join(__dirname, '../files/', key));
});
// 異步等待結(jié)果
const filesBuffer = await Promise.all(files);
// NOTE: 合并切片
const buffer = Buffer.concat(filesBuffer);
// 寫入
fs.writeFileSync(path.join(__dirname, '../files/', filename), buffer);
// 移除切片文件
keys.forEach(key => {
fs.unlink(path.join(__dirname, '../files/', key), function (err) {
if (err) {
throw err;
}
})
});
}
文件容器管理
使用lowdb
存儲文件信息,使用文件md5作為文件id
接口介紹
POST /fileStatus 接口
ReqData
{
"key": "", // 文件Md5
"length": 0, // 切片數(shù)量
"name": "" // 文件名稱
}
ResData
{
"key": "", // 文件Md5
"length": 0, // 切片數(shù)量
"filename": "", // 文件名稱
"blocks": [], // 已完成的切片列表
"url"?: "" // 文件M地址
}
容器數(shù)據(jù)格式
{
"key": "", // md5
"length": 0, // 切片數(shù)量
"filename": "", // 文件名
"blocks": [] // 已完成切片數(shù)量
}
切片信息
{
"index": 0, // 切片下標
"key": "" // 切片 md5 我這里作同時作為切片文件名
}
切片管理
切片上傳成功后需要管理標示,根據(jù)切片下標判斷切片位置如迟,通過文件容器判斷切片上傳狀態(tài),再將切片整合
接口介紹
POST /uploadBlock 接口
ReqData
Form Data
key: 切片id
index: 下標
file: 切片數(shù)據(jù)
parent: 文件容器id
ResData
{
"code":200
}
異步請求優(yōu)化
使用async mapLimit
限制異步任務(wù)數(shù)量攻走,避免一次性請求數(shù)量過多后導致進行中的請求過多殷勘,避免中斷的時候浪費資源過多
// 控制異步并發(fā),避免塊數(shù)量過多失敗的時候?qū)е鲁晒?shù)量變少
// 限制并發(fā)為3個
async.mapLimit(blocks, 3, async (file, callback) => {
// 調(diào)用上傳 塊
await uploadBlcok(file);
}, async () => {
// 單任務(wù)執(zhí)行完成后獲取當前文件狀態(tài),可在這里獲取文件地址
});
如何運行
- 安裝依賴
npm install
- 運行
npm run dev
- 瀏覽器打開
http://127.0.0.1:9870
相關(guān)資料
瀏覽器文件MD5加密
async mapLimit
前端大文件上傳