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

这篇具有很好参考价值的文章主要介绍了前端大文件分片上传 进度条展示 上传暂停、开始、取消。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前端如何控制暂停文件上传,前端功能,前端,javascript,vue
实现的效果:
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);
    }

取消上传是将文件所有请求取消并发送请求删除文件,这里不加赘述。文章来源地址https://www.toymoban.com/news/detail-859028.html

到了这里,关于前端大文件分片上传 进度条展示 上传暂停、开始、取消的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • el-upload实现上传文件并展示进度条

    el-upload在实现文件上传时无法触发on-progress钩子,即使调用后端接口并成功的情况下都无法触发,可以通过如下配置来解决: 随后将config添加至调用后端接口,即可成功获取进度~ html: JS部分

    2024年02月04日
    浏览(36)
  • 前端文件上传(文件上传,分片上传,断点续传)

    普通文件上传 思路: 首先获取用户选择的文件对象,并将其添加到一个 FormData 对象中。然后,使用 axios 的 post 方法将 FormData 对象发送到服务器。在 then 和 catch 中,我们分别处理上传成功和失败的情况,并输出相应的信息。 需要注意,在使用 axios 进行文件上传时,必须将

    2024年02月22日
    浏览(40)
  • 前端上传大文件使用分片上传

    前提:分片上传针对于一些大的文件、普通大小的文件使用element中的上传组件可以实现效果,例如几G的文件就会比较卡,所以这时候就需要用到分片上传~ 前端及后端分片上传笔记 效果:(上传进度展示) 效果:(上传成功的效果展示)

    2024年01月25日
    浏览(43)
  • C#灵活控制多线程的状态(开始暂停继续取消)

    ManualResetEvent 是一个同步基元,用于在多线程环境中协调线程的执行。它提供了两种状态:终止状态和非终止状态。 在终止状态下, ManualResetEvent 允许线程继续执行。而在非终止状态下, ManualResetEvent 会阻塞线程,直到它的状态变为终止状态。 ManualResetEvent 类有以下几个常用

    2024年02月02日
    浏览(37)
  • 前端大文件分片上传

    开始上传:前端启动文件分片上传。后端返回唯一标识。 分片上传:获取到上传的文件,然后设置一个固定的分片大小,将文件切成多个小片,计算出每一个分片的MD5值(32位)。将每个分片的内容和MD5标识符一同上传至服务器。服务端接收每个分片及相关信息后,通过对每

    2024年04月15日
    浏览(33)
  • SpringBoot+前端文件分片上传

    在日常生活中,文件上传相关的操作随处可见,大到处理大数据量的文件,小到头像上传,都离不开文件上传操作,但是当一个文件的大小超过了某个阈值时,这个文件的上传过程就会变得及其的慢,且会消耗大量网络资源,这是我们不愿意看到的,所以,文件分片上传孕育而生。 文件分

    2024年02月19日
    浏览(36)
  • 【JS】前端分片上传大文件(支持1G以上的超大文件)

           如果将大文件一次性上传, 耗时会非常长,甚至可能传输失败 ,那么我们怎么解决这个问题呢?既然大文件上传不适合一次性上传,那么我们可以尝试将文件分片散上传。 这样的技术就叫做分片上传。分片上传就是将大文件分成一个个小文件(切片),将切片进行

    2024年04月11日
    浏览(48)
  • vue(前端):大文件分片上传(包括如何获取文件MD5、逻辑注释讲解)

    3.1 原生input标签实现上传 3.2 获取文件的MD5 3.3 上传文件 3.4 上传文件到后台

    2024年02月21日
    浏览(41)
  • node.js 前端直接分片上传文件与阿里云OSS的方法

    解决问题:直接由用户上传文件至阿里云OSS,而非经过中间件/后端 官方文档:分片上传 (aliyun.com)​​​​​​​​​​​ 在官方文档中,提供的方法是由中间件上传至oss,调用了path库,但是在浏览器用户没有那么大的权限,我们关注到文档中此表: 类型 参数 说明 必选参

    2024年02月11日
    浏览(53)
  • 前端vue elementUI upload上传组件封装&多文件上传&进度条,后端servlet request.getPart()接收文件信息

    选中多个文件上传 通过 axios请求 onUploadProgress 方法监听 on-progress on-success 用这两个钩子函数实现进度条 下面有对应的函数。 本文是每个文件一个请求上传 也可以用一个请求上传多个文件,需要将文件遍历添加到 form 表单中,后端用 request.getParts(); 获取集合,有需要的可以改

    2024年02月11日
    浏览(54)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包