文件分片上传

6 阅读2分钟

一.前言

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.将得到的分片数据(数组),循环上传到服务器。