当前位置:   article > 正文

vue中使用webuploader做断点续传_vue webuploader

vue webuploader


之前做的一个项目中,由于经常上传几百兆的压缩包,导致经常上传失败,所以就找了webuploader插件做了断点续传
断点续传除了需要前端分片,也需要后台去支持,所以做的时候做好对接协调

1、下载webuploader

npm i webuploader
  • 1

2、导入并挂载到原型上

import WebUploader from 'webuploader';
Vue.prototype.WebUploader = WebUploader;
import "webuploader/dist/webuploader.css";
  • 1
  • 2
  • 3

挂载到原型上之后就可以通过 this.WebUploader 去调用
之所以还要导入 webuploader.css 文件,是因为这样在初始化时,才会有正常的按钮样式,样式不符合需求也可以通过自己写样式去覆盖掉,否则初始化之后就只是一个type = file的input框,不方便调整样式

3、md5校验

上传一半取消了,再次上传,怎么接着上传?就需要进行md5校验,后台才知道原来这个文件之前已经上传过了,不需要重头上传
另外,md5的校验必须要写在初始化之前

// 断点续传的按钮初始化之前调用
webUploadBeforeInit(chunkSize) {
  this.WebUploader.Uploader.register({
    "before-send-file": "beforeSendFile",
    "before-send": "beforeSend",
  }, {
      // 时间点1:所有分块进行上传之前调用此函数
      beforeSendFile: (file) => {
        let deferred = this.WebUploader.Deferred();
        let Uploader = new this.WebUploader.Uploader()
        Uploader.md5File(file, 0, chunkSize).progress(percentage => {
            debugger;
            console.log("校验MD5中...")
        }).then(md5 => {
            file.md5 = md5;
            file.uid = this.WebUploader.Base.guid();
            // 进行md5判断
            this.axios({
              url: this.commonUrl + "/FileUploadController/checkFile",
              method: "get",
              params: {
                fileName: file.name,
                md5: file.md5,
              },
            }).then(res => {
                let status = res.code;
                deferred.resolve();
                switch (status) {
                  case 0: // 表示上传成功或者已经上传过了
                    // 忽略上传过程,直接标识上传成功;
                    Uploader.skipFile(file);
                    file.pass = true;
                    break;
                  case 16: // 部分已经上传到服务器了,但是差几个模块。
                    file.missChunks = res.data;
                    break;
                  default:
                    break;
                  // 另外我这里,12表示未上传,1表示添加失败,这里不需要做其他处理
                }
            }).catch((err) => {
              deferred.reject();
            });
        });
        return deferred.promise();
      },
      beforeSend: (block) => {
        let deferred = this.WebUploader.Deferred();
        // 当前未上传分块
        let missChunks = block.file.missChunks;
        // 当前分块
        let blockChunk = block.chunk;
        if (missChunks !== null && missChunks !== undefined &&missChunks !== "") {
          let flag = true;
          for (let i = 0; i < missChunks.length; i++) {
            if (blockChunk === parseInt(missChunks[i])) {
              // 存在还未上传的分块
              flag = false;
              break;
            }
          }
          if (flag) {
            deferred.reject();
          } else {
            deferred.resolve();
          }
        } else {
          deferred.resolve();
        }
        return deferred.promise();
      },
    }
  );
},
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74

4、封装初始化方法

有关webuploader配置项、事件等,可详细参考 WebUploader API文档

webUploadResume(opt, evt){
  if(!(opt && this.isObj(opt))) return console.error("请传类型为对象的配置项opt,pick必传")
  // md5校验在初始化之前
  this.webUploadBeforeInit(opt.chunkSize || 10485760) 

  // 初始化
  let uploader = this.WebUploader.create({
    // 文件上传调的便是此接口
    server: this.commonUrl + "/FileUploadController/breakpointUpload",
    method: opt.method || "post",
    // 这个外部是必传,要选一个dom初始化,无法给默认值。
    // 可以直接给ID,如"#abc",可以是对象,如 
    //  { 
    //    id:"#abc", // 虽然名是id,但实际也可以是类名或标签名
    //    innerHTML:"按钮文字,可不传",
    //    multiple:true/false 是否多选,
    //  }
    pick: opt.pick, // 制定对那个dom进行初始化
    // 指定上传的哪些类型的文件
    // {
    //    title:"文字描述" ,
    //    extensions:"允许的文件后缀,不带点,多个用逗号分割,比如gif,jpg,jpeg,bmp,png",
    //    mimeTypes: "多个用逗号分割,如image/*或者.gif,.jpg,.bmp"
    // }
    accept: opt.accept || null,
    resize: opt.resize || false, // 不压缩img
    auto: opt.auto || true, // 是否开启自动上传
    threads: opt.threads || 1, // 上传并发数。允许同时最大上传进程数
    chunked: opt.chunked || true, // 是否分片上传
    chunkSize: opt.chunkSize || 10485760, // 分片大小,以B为单位,这里是10M = 10 * 1024 * 1024 B
    chunkRetry: opt.chunkRetry || 2, // 如果某个分片由于网络问题出错,允许自动重传多少次
    duplicate: opt.duplicate || true, // 重复上传,为了解决上传一个后若上传失败,需再次上传,会无反应
    formData: opt.formData || {}, // 文件上传请求的额外参数
    fileNumLimit: opt.fileNumLimit || undefined, // 验证文件总数量, 超出则不允许加入队列
    fileSizeLimit: opt.fileSizeLimit || undefined, // 验证文件总大小是否超出限制, 超出则不允许加入队列
    fileSingleSizeLimit: opt.fileSingleSizeLimit || undefined, // 验证单个文件大小是否超出限制, 超出则不允许加入队列
    // 主要看接口是否需要token,不需要的话,下方headers可以去掉
    headers:{
      token: sessionStorage.tkn
    }
  })

  // 绑定事件
  // beforeFileQueued, 当文件被加入队列之前触发
  // fileQueued, 当文件被加入队列以后触发
  // filesQueued, 当一批文件添加进队列以后触发
  // startUpload, 当开始上传流程时触发
  // stopUpload, 当开始上传流程暂停时触发
  // uploadBeforeSend, 当某个文件的分块在发送前触发,主要用来询问是否要添加附带参数
  // uploadProgress, 上传过程中触发,携带上传进度
  // uploadSuccess, 当文件上传成功时触发
  // uploadError, 当文件上传出错时触发
  // uploadComplete, 不管成功或者失败,文件上传完成时触发
  // uploadFinished, 当所有文件上传结束时触发
  // error, 当validate不通过时,会以派送错误事件的形式通知调用者
  // addFail, 当add接口失败后执行(非webuploader插件所带,为自定义)
  if(evt && this.isObj(evt)){
    for(let k in evt){
      if(k == "uploadError" || k == "uploadSuccess"){
        uploader.on(k, (file, resp) => {
          this.axios({
            url:this.commonUrl + "/FileUploadController/add",
            method:"post",
            data:{
              fileName: file.name, // 文件名
              suffix: file.ext, // 文件后缀
              uploadStatus: k == "uploadSuccess"?1:0,
            },
          }).then(res => {
            evt[k](res,file,resp)
          }).catch(err => {
            evt.addFail && evt.addFail(file, resp)
          })
        })
      }else if(k != "addFail"){
        uploader.on(k, evt[k])
      }
    }
  }else{
    console.error("断点续传事件evt未传或传的类型错误")
  }
},
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82

5、调用封装的初始化方法进行初始化

<template>
  <div id="addFail"></div>
</template>

<script>
data(){
  return {}
},
mounted(){
  this.webUploaderInit()
},
methods:{
  webUploaderInit(){
    // 封装的方法也是挂载到原型上的,方便调用
    this.$tu.webUploadResume({
      pick: "#addFail",
      accept: {
        title: "rar,zip",
        extensions: "rar,zip",
        mimeTypes: ".rar,.zip",
      },
      fileSizeLimit: 1073741824, // 总大小不超过1G
    },{
      beforeFileQueued: this.beforeFileQueued,
      uploadProgress: this.uploadProgress,
      uploadSuccess: this.uploadSuccess,
      uploadError: this.uploadError,
      error: this.error,
      addFail: this.addFail,
    })
  },
  beforeFileQueued(file){
    // 注意区分beforeFileQueued和uploadBeforeSend
    // beforeFileQueued是选择文件时,即将加入要上传的文件队列但还没加入时
    // uploadBeforeSend是即将开始调用上传接口,但还没调用
  },
  uploadProgress(file, percentage){
    // percentag,即上传进度
    // 一般用法 percentag * 100 + "%" 或者 percentag * width + "px"
  },
  // 注意我这里uploadSuccess和uploadError跟其他事件注册不太一样,所以接受参数与api文档并不完全一致
  uploadSuccess(res,file,resp){……},
  uploadError(err,file,resp){……},
  error(type){
    // 当 type == "Q_EXCEED_SIZE_LIMIT",就意味着选中的文件总大小超过了设置的fileSizeLimit
    // 当 type == "Q_EXCEED_NUM_LIMIT",就意味着选中的文件数量超过了设置的fileNumLimit
    // 当 type == "Q_TYPE_DENIED",就意味着选中的文件不符合accept中设置的文件格式
    // 另外,如果设置了 fileSingleSizeLimit,即使不符合也不会进入到这里
    // 因为如果选择了A文件超过,B文件未超,那么会将A文件自动过滤掉,只将B加入到文件队列
  },
  // 无论uploadSuccess还是uploadError都会调用add接口,那么add也可能会因为某些情况调用失败
  // 针对此情况,增加了自定义addFail方法,当add接口调用失败时会执行下面方法
  addFail(file, resp){……},
},
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

初始化成功之后,页面便能看到这样一个按钮
在这里插入图片描述
点击即可上传。若样式不符合需求,可以F12找到相关类名,进行样式覆盖
在这里插入图片描述

另外,在覆盖样式时,注意:

  • 你看到的“断点续传”的按钮样式是,类名为webuploader-pick的div
  • 而你点击时,之所以能打开选择文件的弹窗,并非是点击了这个div,实际是点击了下面的lable标签
  • 所以,覆写样式时,注意label标签要和div的位置保持一致,否则就会出现点击但没有任何反应,因为只是点了div,并没有点击到label

参考文档:
WebUploader API文档
vue中大文件上传webuploader前端用法
基于jquery使用webuploader示例

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/66660
推荐阅读
相关标签
  

闽ICP备14008679号