有一天原本平静的前端开发突然受到不明需求的袭击!“快逃!”“可是……” 快找出大文件切片上传的方法,欢迎收看我的开发日记之大文件上传
前言
前端在上传一个较大的文件时, 由于文件过大,需要占用大量的时间,而前端常用的方法是切片上传,也就是将大的文件分成多个数据流, 分别上传至后端,然后再将他们合并为同一个,如此来解决大文件上传时占用时间过长的问题。切片上传简单俩说分为三步:文件切片、文件上传、文件合并。
需要的参数
- file: 单个切片文件流
- md5:文件唯一的id
- chunkTotal: 切片总数
- name: 文件名
- numbel:当前切片在整个切片数组种的位置(建议于文件md5合并生成为同一参数)
- id:每个切片的标识,可以通过总的md5和当前文件在切片数组种的位置合并生成
文件切片
将文件分成我们预先设置的大小
const SIZE = 2 * 1024 * 1024; // 切片大小
createFileChunk(file, size = SIZE) {
const fileChunkList = [];
let cur = 0;
while (cur < file.size) {
fileChunkList.push({ file: file.slice(cur, cur + size) });
cur += size;
}
return fileChunkList;
},
获取文件的md5值
确定我们上传文件的唯一id,利用spark-md5实现,可以利用worker计算
//fileChunkList 切片文件数组
const spark = new self.SparkMD5.ArrayBuffer();
let percentage = 0;
let count = 0;
const loadNext = index => {
// 新建读取器
const reader = new FileReader();
// 设定读取数据格式并开始读取
console.log(fileChunkList[index]);
reader.readAsArrayBuffer(fileChunkList[index].file);
// 监听读取完成
reader.onload = e => {
count++;
// 获取读取结果并交给spark计算hash
spark.append(e.target.result);
if (count === fileChunkList.length) {
self.postMessage({
percentage: 100,
// 获取最终hash
hash: spark.end()
});
self.close();
} else {
percentage += 100 / fileChunkList.length;
self.postMessage({
percentage
});
// 递归计算下一个切片
loadNext(count);
}
};
};
loadNext(0);
文件上传
将切片的文件上传,实际操作种往往需要控制并发量
updataChunkMulti(updataList, multiple = 2){
// apiList 的副本,避免 shift 方法对参数造成影响
let list = [...updataList];
let map = new Map(); //利用map存返回值
//let count = 0; //记录已上传的文件数量,用于进度条
// 递归调用
const run = () => {
if (list.length) {
const params = list.shift();
let formData = new FormData();
Object.keys(params).map(key=>{ //将参数改为后端需要的形式
console.log(key);
formData.append(key, params[key]);
});
return postAction(SystemManageApi.notice.uploadChunkFile,formData).then(res => { //文件上传接口
map.set(params, res);
//count++;
//this.percentage = Number.parseInt(count/updataList.length * 100); //percentage为进度条
return run();
}).catch(res=>{ //用于捕获单个切片文件上传失败
map.set(params, res);
return Promise.reject(res); //单个文件上传失败后不在继续上传
//return run() //单个文件上传后继续上传
});
}
return null;
};
//控制并发
const promiseList = Array(Math.min(updataList.length, multiple)).fill(Promise.resolve()).map(promise => promise.then(run));
return Promise.all(promiseList).then(() => {
return updataList.map(c => map.get(c));
}).catch((res)=>{
return Promise.reject();
});
},
文件合并
调通合并接口, 通知后端将上传的切片文件进行合并
结尾
本文仅仅只是文件上传的基本步骤,实际项目中的文件切片上传,往往还有断点续传、暂停、文件秒传功能,其中断点续传和文件秒传,都是通过后端通过md5鉴别当前文件是否已经上传还是已经上传了部分切片,而上传暂停则是利用axios实现。桀桀桀,前端大文件切片上传不知道你会了没???