一.前言
1.首先来打个比方,假如服务器是个嘴巴,文件是个饼。
eg:传统方式是将饼直接喂给服务器---------直接上传。
eg:分片上传是将饼划分成多块,一小块一小块喂给服务器。
2.对于传统的上传方式,会存在以下问题
- 饼太大,网络和服务器承受不住,导致接口请求超时,上传失败。
- 当网络比较差的时候,上传文件也会存在上述问题。
- 当用户提交上传时,上传到什么程度了,还需要多久等等的都不能体现出来,尤其等待的时间长会导致用户体验感极差。
2.为解决以上问题,先提供以下实现方案
- 把饼划分成一小块一小块的喂给服务器
二.方案实现
1.将文件拆分,file(源文件),multiplier(多少M)
function sliceFileAndGetMd5(file, multiplier = null) {
const CHUNK_SIZE = 1 * 1024 * 1024; // 1M
return new Promise((resolve) => {
const chunks = [] as any; // 存储所有分片Blob
const chunkList = [] as any; // 存储分片详细信息
const fileSize = file.size;
let current = 0; // 当前分片起始字节
const spark = new SparkMD5.ArrayBuffer(); // 创建spark-md5实例
const fileReader = new FileReader(); // 读取分片内容
// 读取分片完成的回调(核心:流式累加计算MD5)
fileReader.onload = function (e: any) {
// 将分片的二进制内容加入spark-md5,累加计算
spark.append(e.target.result);
current += (multiplier || 10) * CHUNK_SIZE;
// 判断是否分片完成
if (current < fileSize) {
loadChunk(); // 未完成,继续读取下一个分片
} else {
// 所有分片读取完成,得到文件完整MD5
const fileMd5 = spark.end();
resolve({ chunks, fileMd5, chunkList });
}
};
// 分片读取核心方法
function loadChunk() {
// 截取当前分片:[current, current+CHUNK_SIZE]
const end = Math.min(current + (multiplier || 10) * CHUNK_SIZE, fileSize);
const chunk = file.slice(current, end);
chunks.push(chunk);
chunkList.push({
index: chunks.length - 1, // 分片索引
start: current,
end: end,
size: end - current,
});
// 读取分片为ArrayBuffer格式(spark-md5推荐格式,效率高)
fileReader.readAsArrayBuffer(chunk);
}
// 启动分片读取
loadChunk();
});
}
2.将得到的分片数据(数组),循环上传到服务器。