您现在的位置是:首页 > 经典句子

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

作者:胡椒时间:2024-04-17 16:29:34分类:经典句子

简介  文章浏览阅读905次。实现的效果:1、多个大文件(支持10个G以上)分片上传2、进度条展示进度3、控制文件上传暂停和取消_前端如何控制暂停文件上传

点击全文阅读

在这里插入图片描述
实现的效果:
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);      });}

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

<div  class="bigFileProgress"  v-for="(f, index) in bigFileList"  :key="index">  <div class="bigFileTop">    <Tooltip :content="f.file.name" placement="top">      <div class="bigFileName">        {{ f.file.name }}      </div>    </Tooltip>    <div class="bigFileSize" style="width: 20%">      {{ f.newSize }}    </div>    <!-- <div class="bigFileUploadProgress" style="width: 5%">      {{ f.progress }}%    </div> -->    <div      class="bigFileStatus"      :style="{        width: '20%',        color: f.status == '上传失败' ? 'red' : 'black',      }"    >      {{ f.status }}    </div>    <div      class="bigFileActions"      style="position: relative; width: 20%"    >      <Button        @click="pauseBigFile(f)"        type="primary"        size="small"        style="position: absolute"        v-if="!f.isPaused"        :disabled="f.progress == 100"      >        暂停      </Button>      <Button        @click="restartBigFile(f)"        type="primary"        size="small"        :disabled="f.progress == 100"        :style="{ opacity: f.progress == 100 ? 0 : 1 }"      >        开始      </Button>      <Button        @click="cancelUpload(f)"        type="error"        size="small"        style="margin-left: 10px"      >        取消      </Button>    </div>  </div>  <div class="progress">    <Progress :percent="f.progress" :stroke-width="5"></Progress>  </div></div>

分片上传

首先查询文件已经上传的分片数,如果全部上传了,进度立即更新为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);    }

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

点击全文阅读

郑重声明:

本站所有活动均为互联网所得,如有侵权请联系本站删除处理

我来说两句