前端大文件分片上传 进度条展示 上传暂停、开始、取消 您所在的位置:网站首页 前端上传文件显示进度条 前端大文件分片上传 进度条展示 上传暂停、开始、取消

前端大文件分片上传 进度条展示 上传暂停、开始、取消

2024-06-16 18:26| 来源: 网络整理| 查看: 265

在这里插入图片描述 实现的效果: 1、多个大文件(支持10个G以上)分片上传 2、进度条展示进度 3、控制文件上传暂停和取消 实现关键点: 1、文件预处理(md5计算、请求和进度处理等) 2、分片上传的流程(查询已上传分片、文件合并等) 3、文件的暂停、开始、取消

文件预处理

首先使用file类型的input框获取文件,对文件进行深拷贝,再清空input的value值(防止input的change事件不被触发)。

let files = e.target.files; let copiedFiles = [] for(let i = 0; i < files.length; i++){ copiedFiles.push(new File([files[i]], files[i].name, { type: files[i].type })) } this.$emit("bigFileChange", copiedFiles); this.$refs.input.value = null;

对文件进行处理,核心思想是为每个文件构造一个对象,封装该文件的md5信息(用于标识该文件)和进度、请求、取消标识(用于文件的暂停)等信息。

async bigFileChange(files) { // 新增的文件 let newFiles = []; // 筛选出检验合格的文件 let okFileIndexs = this.checkRules(files); for (let i = 0; i < okFileIndexs.length; i++) { let fileIndex = okFileIndexs[i]; // 为文件构建对象 let fileObj = {}; fileObj.md5 = await this.firstChunkMd5( files[fileIndex], this.chunkSize ); fileObj.progress = 0; fileObj.isPaused = false; // 查询该文件合并进度的轮询计时器 fileObj.mergeTimer = null; fileObj.status = "上传中"; fileObj.newSize = this.getFileSize(files[fileIndex].size); fileObj.file = files[fileIndex]; fileObj.category = this.category; // 该文件的所有请求 fileObj.requests = []; // 该文件的取消标识 fileObj.cancelTokens = []; // 将构建的对象记录下来 newFiles.push(fileObj); this.bigFileList.push(fileObj); } for (const newFileObj of newFiles) { this.uploadBigAttachment(newFileObj); } }

计算md5值采用的是SparkMD5,为了减少计算量,采用文件的第一块的md5作为整个文件的md5。

firstChunkMd5(file, chunkSize) { return new Promise((resolve, reject) => { const fileReader = new FileReader(); const spark = new SparkMD5.ArrayBuffer(); const chunk = file.slice(0, chunkSize); fileReader.onload = function (event) { spark.append(event.target.result); const md5 = spark.end(); resolve(md5); }; fileReader.onerror = function () { reject(new Error("File read error.")); }; fileReader.readAsArrayBuffer(chunk); }); }

在界面上为每个文件创建进度条。

{{ f.file.name }} {{ f.newSize }} { f.progress }}% --> {{ f.status }} 暂停 开始 取消 分片上传

首先查询文件已经上传的分片数,如果全部上传了,进度立即更新为100%(秒传),如果没完全上传,则上传未上传的分片并实时更新进度,各分片上传完毕后请求合并,采用轮询检测合并进度。

this.checkFile(fileObj, chunks) .then(async (res) => { console.log(res); if (res.data.data.completed) { // 如果当前文件已经上传成功 则无需继续上传 fileObj.progress = 100; fileObj.status = "上传成功"; // 为成功上传的附件添加id if (res.data.data.attachmentId) { fileObj.attachmentId = res.data.data.attachmentId; } this.$forceUpdate(); // 强制重新渲染组件 this.$emit("fileUpdate"); } else { // 当前文件没有上传成功 // 获取已经上传的分片数组 let uploadedChunks = res.data.data.uploadChunks; // 获取当前的进度 let newProgress = parseInt( (uploadedChunks.length / chunks) * 100 ); fileObj.progress = newProgress; this.$forceUpdate(); // 强制重新渲染组件 // 文件均已上传完 但还未合并 if (res.data.data.canMerge || uploadedChunks.length == chunks) { this.mergeBigFile(fileObj) .then((res) => { fileObj.status = "合并中"; this.$forceUpdate(); // 强制重新渲染组件 // 先清除该文件上次的合并计时器 if (fileObj.mergeTimer) { clearInterval(fileObj.mergeTimer); } fileObj.mergeTimer = setInterval(() => { this.getMergeProcess(fileObj).then((res) => { if (res.data.data.completed) { fileObj.status = "上传成功"; // 为成功上传的附件添加id if (res.data.data.attachmentId) { fileObj.attachmentId = res.data.data.attachmentId; } this.$forceUpdate(); // 强制重新渲染组件 // 合并完成 fileObj.requests = []; fileObj.cancelTokens = []; clearInterval(fileObj.mergeTimer); this.$emit("fileUpdate"); } }); }, 2000); }) .catch((error) => { console.error("上传失败:", error); fileObj.status = "上传失败"; if (fileObj.mergeTimer) { clearInterval(fileObj.mergeTimer); } this.$forceUpdate(); }); } else { // 文件还没上传完 let currentChunk = 0; // 上传没有上传的部分 while (currentChunk < chunks) { if (!uploadedChunks.includes(currentChunk)) { const start = currentChunk * this.chunkSize; const end = Math.min( start + this.chunkSize, fileObj.file.size ); const chunk = fileObj.file.slice(start, end); // 构造该块的上传请求 const formData = new FormData(); let fileType = fileObj.file.name.substring( fileObj.file.name.lastIndexOf(".") + 1 ); formData.append("fileName", fileObj.file.name); formData.append("fileType", fileType); formData.append("md5", fileObj.md5); formData.append("category", fileObj.category); formData.append("ownerType", "bill"); formData.append("ownerId", this.billidParam); formData.append("chunkNum", currentChunk); formData.append("chunkSize", this.chunkSize); formData.append("chunkTotal", chunks); formData.append("file", chunk); // 该块的取消令牌 let cancelToken = axios.CancelToken.source(); fileObj.cancelTokens.push(cancelToken); let request = GMS.$http.post( "/bsp/bjgzw/attachment/uploadChunk", formData, { headers: this.headers, cancelToken: cancelToken.token, } ); fileObj.requests.push(request); } currentChunk++; } // 当前文件下的所有请求 for (let i = 0; i < fileObj.requests.length; i++) { fileObj.requests[i] .then((res) => { console.log(res); // 进行进度控制 let progress = parseInt( (res.data.data.uploadChunks.length / chunks) * 100 ); if (progress > fileObj.progress) { fileObj.progress = progress; this.$forceUpdate(); // 强制重新渲染组件 } // 进行文件的合并控制 if (res.data.data.canMerge) { // 文件可以合并了 this.mergeBigFile(fileObj) .then((res) => { fileObj.status = "合并中"; this.$forceUpdate(); // 强制重新渲染组件 // 先清除该文件上次的合并计时器 if (fileObj.mergeTimer) { clearInterval(fileObj.mergeTimer); } fileObj.mergeTimer = setInterval(() => { this.getMergeProcess(fileObj).then((res) => { if (res.data.data.completed) { fileObj.status = "上传成功"; // 为成功上传的附件添加id if (res.data.data.attachmentId) { fileObj.attachmentId = res.data.data.attachmentId; } this.$forceUpdate(); // 强制重新渲染组件 // 合并完成 fileObj.requests = []; fileObj.cancelTokens = []; clearInterval(fileObj.mergeTimer); this.$emit("fileUpdate"); } }); }, 2000); }) .catch((error) => { console.error("上传失败:", error); fileObj.status = "上传失败"; if (fileObj.mergeTimer) { clearInterval(fileObj.mergeTimer); } this.$forceUpdate(); }); } }) .catch((error) => { if (axios.isCancel(error)) { console.log("上传已暂停或取消"); } else { console.error("上传失败:", error); fileObj.status = "上传失败"; this.$forceUpdate(); } fileObj.requests = []; fileObj.cancelTokens = []; }); } } } }) .catch((error) => { console.error("Error:", error); fileObj.status = "上传失败"; this.$forceUpdate(); }); 上传暂停、开始、取消

暂停上传即根据取消标识将当前文件的所有请求进行取消。

pauseBigFile(fileObj) { fileObj.cancelTokens.forEach((item) => { item.cancel("上传暂停"); }); fileObj.isPaused = true; fileObj.status = "已暂停"; this.$forceUpdate(); // 强制重新渲染组件 }

开始上传即对文件重新进行上传处理。

restartBigFile(fileObj) { fileObj.isPaused = false; fileObj.status = "上传中"; this.$forceUpdate(); // 强制重新渲染组件 fileObj.requests = []; fileObj.cancelTokens = []; this.uploadBigAttachment(fileObj); }

取消上传是将文件所有请求取消并发送请求删除文件,这里不加赘述。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有