vue el-upload 大文件切片上传 带进度条

概要

项目中经常会遇到文件 size 特别大的 动不动就 1g、2g 这样在上传过程中就会

文件过大,超出服务端的请求大小限制;

请求时间过长,请求超时;

传输中断,必须重新上传导致前功尽弃;

补充:

前端上传文件总体来说常用的两种方式:二进制传输和base64格式直接传输

正文开始之前先简单认识一下文件上传的四个相关对象:

1.files对象:

可以通过指定input标签type属性为file来读取files对象,是一个由一个或多个文件对象组成的数组。同时也是blob对象的子类,继承了一些blob对象的方法

2.blob对象:

表示二进制类型的大对象。在数据库管理系统中,将二进制数据存储为一个单一个体的集合。Blob 通常是影像、声音或多媒体文件。在 JavaScript 中 Blob 类型的对象表示不可变的类似文件对象的原始数据, 使用构造函数创建。

3.formData对象:

FormData就是 XMLHttpRequest Level 2 新增的一个对象,利用它来提交表单、模拟表单提交,最大的优势就是可以上传二进制文件。

作用1:模拟HTML表单,相当于将HTML表单映射成表单对象,自动将表单对象中的数据拼接成请求参数的格式。
作用2:异步上传二进制文件。
 

4.fileReader对象

构造函数方式实例化一个fileReader对象,readAs()方法将文件对象读取成base64格式或者文本格式

解决思路

将获取到的文件 二进制流 按照3兆每片(自定义) 切割成若干片 将这些片依次传给后端 后端在将这些切片拼接起来组合成一个完整的文件  一般大文件上传 会进行md5 计算校验

什么是md5 ?

在选择文件获取到文件之后,首先是对文件进行MD5值的计算,然后拿着这个MD5值对查询后端接口此文件是否存在。查询结果分为三种情况,一是不存在,二是已存在,三是部分存在。不存在时对文件分块,然后一块一块上传;已存在时直接使用已存储的文件,即秒传;部分存在时后端会返回不存在的文件块的位置,然后上传对应不存在的块。

代码展示

<el-upload
                action=""
                :http-request="httprequest"
                list-type="picture-card"
                :on-change = 'onchange'
                :file-list="getinfodata.logFiles">
                  <i slot="default" class="el-icon-upload"></i>
                  <div slot="file" slot-scope="{file}">
                    <div style="padding: 10px;box-sizing: border-box;word-wrap: break-word;font-size: 12px;">
                      <div>
                        <div>
                          文件名称: 
                          <div>
                            {{file.name}}
                          </div> 
                        </div>
                        <div style="font-size: 12px;">
                          文件大小: {{ getfilesize(file) }}
                        </div>
                        <el-progress 
                          v-show="files && file.uid == files.uid && chunkindex<100" 
                          :format="formats" 
                          :color="colors" 
                          :percentage="chunkindex"
                          :text-inside="true"
                          text-color="green"
                          :stroke-width="18"
                          style="margin-top: 50px;">
                        </el-progress>
                      </div>
                    </div> 
                    <span class="el-upload-list__item-actions">
                      <span
                        class="el-upload-list__item-delete"
                        @click="handleDownload(file)">
                        <i class="el-icon-download"></i>
                      </span>
                      <span
                        class="el-upload-list__item-delete"
                        @click="handleRemove(file)">
                        <i class="el-icon-delete"></i>
                      </span>
                    </span>
                  </div>
              </el-upload>
// 覆盖组件默认的上传行为,可以自定义上传的实现
      httprequest(file){
              // 每个文件切片大小定义为 5 MB
              let sliceSize = 3 * 1024 * 1024;
              this.calculateMD5(file.file,sliceSize).then(async (md5) => {
                  console.log('File MD5:', md5);
                  let blob = file.file;
                  const {size:fileSize,name:fileName} = blob;// 文件大小 // 文件名称
                  //计算文件切片总数,Math.ceil向上取整数
                  const totalSlice = Math.ceil(fileSize / sliceSize);
                  this.chunktotle = totalSlice;
                  console.log('当前上传文件的详情信息',blob,totalSlice, fileSize / sliceSize)
                  // 循环上传
                  // 作用域:使用var声明的变量具有函数作用域,而使用let声明的变量具有块级作用域。函数作用域意味着变量在整个函数体内都是可见的,而块级作用域只在当前代码块内有效。
                  // 声明提升:使用var声明的变量会在其所在作用域的顶部进行声明提升,也就是说在变量声明之前就可以使用它,但其值为undefined。而使用let声明的变量不会进行声明提升,它只能在声明之后才能被访问到。
                  for(let i = 0; i< totalSlice; i++){
                      let start = i * sliceSize;
                      let chunk = blob.slice(start,Math.min(fileSize, start + sliceSize))
                      console.log('每个切片的信息:',chunk)
                      const formData = new FormData();
                      // 根据后端需要的参数去自定义formData的参数名和值
                      formData.append('chunk',chunk); // 分片后的文件流
                      formData.append('index',i+1); // 分片索引
                      formData.append('total',totalSlice); // 共多少个分片
                      // 在前端进行大文件上传时,通常可以使用 MD5 值计算来验证文件的完整性。
                      // MD5(Message Digest Algorithm 5)是一种常用的哈希算法,
                      // 它将任意长度的数据转换为固定长度的唯一哈希值
                      // 在大文件上传的场景中,前端会将文件分割为多个小块,然后逐个上传这些小块,最后合并成完整的文件。
                      // 为了确保每个小块在传输过程中没有被篡改或丢失,需要进行校验。
                      // MD5 值计算的过程如下:
                        // 将待上传的文件进行分块。
                        // 对每个分块的数据进行 MD5 值计算。
                        // 将计算得到的 MD5 值上传到服务器。
                        // 服务器接收到每个分块后,也对接收到的分块数据进行 MD5 值计算。
                        // 服务器将计算得到的 MD5 值与前端上传的 MD5 值进行比较,如果一致则说明分块数据传输正常,否则需要重新传输该分块数据。
                      formData.append('fileMd5',md5); // 整体文件的MD5值
                      formData.append('partSize',chunk.size); // 整体文件的MD5值
                      formData.append('uploadId',res.data.upload_id); // init_upload返回的id
                      formData.append('complete',(i+1) == totalSlice?'true':this.complete == 'cancel'?'cancel':''); // (true最后一分片)/(cancel取消分片上传)
                      // 调取后端接口 进行分片上传
                      let resresult = await this.postRequest('/case/chunk/upload',formData);
                      console.log(i+1,'--------------',resresult);
                      // 下面代码可以根据自己的业务情况写
                      if(resresult.data.cancel == 0){
                        this.chunkindex = 0;
                        this.complete = '';
                        break; // 当点击删除切片请求会返回 cancel 字段此时不需要在请求接口提交后续切片数据退出循环即可
                      }
                      if(resresult.code == 0){
                        console.log(Math.ceil((i+1)/totalSlice*100));
                        console.log((i+1)/totalSlice);
                        this.chunkindex = Math.ceil((i+1)/totalSlice*100); // 记录进度条当前到了第几个切片
                        if(resresult.data.url){
                            this.fileUrl.push({name:fileName,url:resresult.data.url,size:fileSize,uid:file.file.uid});
                        }
                      }else{
              this.$message.error({duration:0,message:resresult.message,showClose:true});
                        break;
                      }
                  }
              })
              .catch((error) => {
                this.$message.error(error);
              });
            }      
},

 

 // md5 的计算
      calculateMD5(file,chunkSize) {
        return new Promise((resolve, reject) => {
          const chunks = Math.ceil(file.size / chunkSize);
          let currentChunk = 0;
          const spark = new SparkMD5.ArrayBuffer();

          const fileReader = new FileReader();

          fileReader.onload =  (e)=> {
            spark.append(e.target.result); // 更新 MD5 值
            currentChunk++;
            if (currentChunk < chunks) {
              this.loadNextChunk(fileReader,currentChunk,chunkSize,file);
            } else {
              const md5 = spark.end(); // 计算最终 MD5 值
              resolve(md5);
            }
          };
          fileReader.onerror = function (e) {
            reject(e);
          };
          this.loadNextChunk(fileReader,currentChunk,chunkSize,file);
        });
      },
      // md5 计算
      loadNextChunk(fileReader,currentChunk,chunkSize,file) {
          const start = currentChunk * chunkSize;
          const end = Math.min(start + chunkSize, file.size);
          const chunk = file.slice(start, end);
          fileReader.readAsArrayBuffer(chunk);
      },

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在el-upload组件中自定义上传文件进度条显示,需要使用自定义上传的方式。具体步骤如下: 1. 使用自定义上传的方式,可以通过覆盖默认的上传行为来实现。这样可以确保on-progress事件生效。 引用<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [el-upload自定义上传文件显示进度条](https://blog.csdn.net/weixin_43363871/article/details/126769620)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [elementUI el-upload自定义上传文件,不用action属性](https://blog.csdn.net/Da_Xiong000/article/details/125907055)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [vue-cli3.0+element-ui上传组件el-upload的使用](https://download.csdn.net/download/weixin_38725015/13200387)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值