如果你不是被迫使用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'// 导入webuploaderimport 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>