vue3+vite+WebUploader文件上传

如果你不是被迫使用WebUploader的,可以离开这篇文章去找其他适合vue3的文件上传框架了,如果你已经进了这个坑,下面的介绍希望能让你少踩点坑。

1. 安装

安装webuploader :npm install webuploader --save

webuploader依赖jq,安装jquery(我这里指定了jq的版本,没什么特别的原因...):npm install jquery@3.5.1

2. vite构建的项目注意

vite构建的项目不支持非严格模式,需要修改node_modules\webuploader\dist\webuploader.js和webuploader.fis.js 文件(改完需要删掉node_modules里面的.vite,然后重新运行):

    原来:
    // input.on( 'change', function( e ) {
    //     var fn = arguments.callee,
    //         clone;

    //     me.files = e.target.files;

    //     // reset input
    //     clone = this.cloneNode( true );
    //     clone.value = null;
    //     this.parentNode.replaceChild( clone, this );

    //     input.off();
    //     input = $( clone ).on( 'change', fn )
    //             .on( 'mouseenter mouseleave', mouseHandler );

    //     owner.trigger('change');
    // });

    改为:
    var changeFn = (function even(that, e){

        var clone;

        me.files = e.target.files;

        // reset input
        clone = that.cloneNode( true );
        clone.value = null;
        that.parentNode.replaceChild( clone, that );

        input.off();
        input = $(clone).on('change', function(e){
            even(this, e);
        }).on('mouseenter mouseleave', mouseHandler);

        owner.trigger('change');
    });

    input.on('change', function(e){
        changeFn(this, e);
    });

3. 创建WebUploader.vue组件

注意下面的message.xx是已经被二次封装的Elmessage消息提示

<template>
  <div class="WebUploader">
    <!-- 选择文件按钮 -->
    <div class="UploadFile__chooseFile">{{butTextC}}</div>
  </div>
</template>
<script setup>
import { ref, onMounted, nextTick} from 'vue'
import { message } from '@/assets/util.js'
import { getVerify } from '@/service/api'
// 导入webuploader
import webUploader from 'webuploader'

const props = defineProps({
  butTextC: {
    type: String,
    default: '选择文件'
  }
})

const emit = defineEmits([
  'butClickC' // 用于告诉父组件点击了按钮
])

// 避免webUploader.Uploader.register重复注册
webUploader.Uploader.unRegister('UploadFileRegister')

register()

//监听分块上传过程中的三个时间点(开启分片上传时有效,我只用到了beforeSendFile,所以没有后面两块的代码), 需要在webUploader.create之前调用
function register() {
  webUploader.Uploader.register(
    {
      'name': 'UploadFileRegister',
      'before-send-file': 'beforeSendFile',
      'before-send': 'beforeSend',
      'after-send-file': 'afterSendFile'
    },
    {
      // 时间点1::所有分块进行上传之前调用此函数
      beforeSendFile: function (file) {
        // 利用md5File()方法计算文件的唯一标记符
        // 创建一个deffered
        let deferred = webUploader.Deferred()
        // 1.md5File:计算文件的唯一标记,把文件名文件大小和md5拼接作为文件唯一标识,避免重复上传
        new webUploader.Uploader()
          .md5File(file, 0, 10 * 1024 * 1024)
          .progress(function (percentage) {})
          .then(function (val) {
            file.fileMd5 = val
            
            // 允许自定义file.xx插入文件的对象里,可以在下文的fileQueued等阶段的file中取到
            // file.test = xx
            
            // 如果文件后缀是禁止上传的后缀,跳过上传
            // 这是由于我没有找到webUploader过滤上传后缀的属性,所以在这里处理,excludeFile内容为[doc,docx]这种
            let flagForbid = 1
            store.excludeFile.forEach(item => {
                if(item === file.ext) {
                  flagForbid = 2
                  deferred.reject()
                  message.warning(`不允许上传.${file.ext}后缀的文件!`)
                }
              })
            if(flagForbid === 2) return

            // 2.请求后台是否保存过该文件,如果存在,则跳过该文件,实现秒传功能,resolve:继续上传;reject:跳过文件,这里getVerify是本项目校验文件的一个接口,如果你们不需要校验文件是否后台有重复,可以不用这块
            getVerify(data)
              .then((res) => {
                if (res.success) {
                  deferred.resolve()
                } else {
                  message.error(data.name + res.message)
                  // 文件校验失败
                  deferred.reject()
                }
              })
              .catch(() => {
                message.error('文件校验失败')
                // 验证接口报错,跳过该文件
                deferred.reject()
              })
          })
        //返回deffered
        return deferred.promise()
      }
    }
  )
}

// 将fileType处理成.doc,.docx,(给前面多加一个.)
function buildFileType(fileType) {
  let ts = fileType
  let ty = ''
  for (let i = 0; i < ts.length; i++) {
    ty = ty + '.' + ts[i] + ','
  }
  return ty.substring(0, ty.length - 1)
}

onMounted(() => {
  // webUploader.create要在页面渲染完成后进行,不然会找不到.UploadFile__chooseFile,创建不了按钮
  nextTick(() => {
    uploader = webUploader.create({
      auto: false,
      resize: false, // 不压缩image
      duplicate: true, // 文件去重,默认开启,不允许用户选择同一个文件
      server: apiUrl + 'xx/xx', // 默认文件接收服务端地址
      pick: {
        id: '.UploadFile__chooseFile', // 指定选择文件的按钮容器,不指定则不创建按钮。它不仅支持id, 也支持class、dom节点(这个没有试过)。
        multiple: true // 开启文件多选
      },
      accept: [
        {
          title: 'file',
          extensions: store.includeFile[0] ? store.includeFile : '*', // 上传接受的文件类型,'*'表示所有都接受,extensions接受的参数为:[doc,docx,pdf,xls]这种形式
          mimeTypes: store.includeFile[0] ? buildFileType(store.includeFile) : '*', // 指上传窗口默认展示的匹配文件后缀的文件(就是弹出的上传窗口,它可能存在很多类型的文件,如果你只写了.doc,那么此时只会显示.doc的文件,其他过滤掉不显示):参数为'.doc,.docx,.pdf,.xls'这种形式
        }
      ],
      fileNumLimit: 10, // 验证文件总数量, 超出则不允许加入队列,默认值:undefined,如果不配置,则不限制数量
      fileSizeLimit: 100 * 1024 * 1024 * 1024, // 1kb=1024*1024,验证文件总大小是否超出限制, 超出则不允许加入队列
      fileSingleSizeLimit:  100 * 1024 * 1024 * 1024, // 验证单个文件大小是否超出限制, 超出则不允许加入队列。
      chunked: true, // 是否开启分片上传
      chunkSize: 100 * 1024 * 1024 * 1024, // 如果要分片,每一片的文件大小,分片需要后端配合,我的项目没有,但是我又需要beforeSendFile阶段,所有我开启了分片上传,然后给每个分片为接受的文件的最大值,它就不会被真的分片了
      prepareNextFile: false // 在上传当前文件时,准备好下一个文件,设置成false,不然开启文件多选浏览器会卡死
    })

    // 文件加入队列后
    uploader.on('fileQueued', function (file) {
       // 文件上传
       uploader.upload()
       // 文件加入队列告诉父组件关闭弹窗
       emit('butClickC', 1)

       // 这个属性可以移除当前的文件上传,你可以自行判断什么情况下移除,注意文件加入队列是一个一个加入的,即每加入一个都有自己的uploader.on('fileQueued')方法,当前的fileQueued只能处理当前文件
       // uploader.removeFile(file, true)
    })

    // 上传之前
    uploader.on('uploadBeforeSend', function (block, data, headers) {
      // 注意,加入队列的文件(fileQueued)不一定都被上传了(可以在beforeSendFile时间点跳过),但uploadBeforeSend阶段的都是允许上传的文件
     // 允许自定义block.file.xx插入文件的对象里,可以在下文的后续阶段的file中取到
     // beforeSendFile时间点定义的file.test = xx,在这里可以用block.file.test取到
     // block.file.testA = 'xx'
    })

    // 文件上传中
    uploader.on('uploadProgress', function (file, percentage) {
        // 可以根据此时的percentage显示进度条
    })

    // 文件上传成功
    uploader.on('uploadSuccess', function (file, response) {
      message.success(file.name + '文件上传成功')
    })

    // 文件上传失败
    uploader.on('uploadError', function (file, reason) {
      message.warning(file.name + '文件上传失败')
    })

    // 文件上传错误
    uploader.on('error', function (handler) {
      if (handler == 'F_EXCEED_SIZE') {
        message.warning('文件上传过大!')
      } else if (handler == 'Q_EXCEED_SIZE_LIMIT') {
        message.warning('文件大小超过限制!')
      } else if (handler == 'Q_TYPE_DENIED') {
        message.warning('不允许上传此类文件!')
      }
    })

    // 所有文件上传完成,无论成功或者失败
    uploader.on('uploadFinished',function(){
    });
  })
})
</script>
<style lang="scss">
// webuploader 初始化样式,注意不能写在scope里面
.webuploader-container {
  position: relative;
}
.webuploader-element-invisible {
  position: absolute !important;
  clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
  clip: rect(1px, 1px, 1px, 1px);
}
.webuploader-pick {
  // margin-left: 0.12rem;
  // position: relative;
  display: block;
  cursor: pointer;
  background: var(--el-color-primary);
  // padding: 0.06rem 0.15rem;
  color: #fff;
  text-align: center;
  border-radius: 0.04rem;
  overflow: hidden;
  width: 1rem;
  height: 0.31rem;
  line-height: 0.31rem;
  border: none;
}

.webuploader-pick-hover {
  background: var(--el-color-primary-light-3);
}

.webuploader-pick-disable {
  opacity: 0.6;
  pointer-events: none;
}
</style>

4. 使用WebUploader.vue组件
<template>
  <div class="UploadFile__wrap">
    <el-dialog
      :model-value="isVisibleC"
      :show-close="false"
      @close="close"
      title="上传文件"
    >
      <template #footer>
        <div class="dialog-footer">
          <el-button class="dialog-footer-cancel" @click="cancel">取消</el-button>
           
          <CpnWebUploader @butClickC='butClickC' :butTextC='选择上传的文件'></CpnWebUploader>
        </div>
      </template>
    </el-dialog>
  </div>
</template>

<script setup>
import CpnWebUploader from '@/components/common/WebUploader.vue';

// 子组件发射的@butClickC事件这里处理
function butClickC() {
}

</script>

lang.StringBuffer] count=0 它意味着什么? 这个错误信息意味着在使用Java反射机制创建对象时,传递给构造函数的参数类型与期望的不一致。在这个错误信息中,期望的构造函数的参数类型是[java.lang.StringBuffer],但实际传递的参数类型是[count=0]。 Java中的反射机制允许程序在运行时动态地获取类的信息并操作类的属性和方法。其中一个常见的应用是通过反射创建对象。在创建对象时,需要指定要调用的构造函数和传递给构造函数的参数。 错误信息中的"java.lang.reflect.constructor expected=[java.lang.StringBuffer]"表示期望的构造函数是接受一个参数类型为java.lang.StringBuffer的构造函数。 而"count=0"表示实际传递的参数的类型是一个名为count的类,且其值为0。 可能发生这种错误的原因可能包括: 1. 在创建对象时,代码中传递给构造函数的参数类型错误,不符合期望的构造函数参数类型。 2. 构造函数的期望参数类型与代码中指定的构造函数不匹配。 3. 在使用反射创建对象时,构造函数的期望参数类型与所调用的构造函数的参数类型不一致。 为了解决这个问题,需要检查代码中创建对象的部分并确保正确地传递适当的参数类型给构造函数。同时,需要确认所调用的构造函数的参数类型与期望值一致。 请注意,上述回答仅基于错误信息的字面意思,具体的解决方案还需要根据具体的代码情况进行分析和调试。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值