-
不再对使用问题进行答复,如果有希望的功能改进或者bugfix可以提交PR WebUploader 文件上传 WebUploader是一个简单的以HTML5为主,FLASH为辅的现代文件上传组件。在现代的浏览器里面能充分发挥HTML5的优势,同时...
-
vue结合WebUploader插件实现大文件分片上传
2020-03-01 16:22:251.下载webuploader 下载地址:官网http://fex.baidu.com/webuploader/ 2.引入webuploader ...这里我们使用完全压缩版的webuploader.min.js,同时还要引入Uploader.swf 、 webuploader.css,所以可以删除...1.下载webuploader
下载地址:官网http://fex.baidu.com/webuploader/
2.引入webuploader
使用Web Uploader文件上传需要引入三种资源:JS, CSS, SWF。
方法1.直接引入文件
这里我们使用完全压缩版的webuploader.min.js,同时还要引入Uploader.swf 、 webuploader.css,所以可以删除其他版本的文件,只留这三个文件,将解压的webuploader文件夹直接放在static文件夹下.在index.html中引入webuploader.min.js
<script src="./static/webuploader-0.1.5/webuploader.min.js"></script>
重启项目后报错了,说缺少依赖jquery,打开源码webuploader.js看一下
原来webuploader是依赖于juqery或者Zepto的,那我们引入jquery,我们去jquery官网下载jquery,并在index.html中引入,注意,一定要在webuploader.min.js之前引入,在这里我下载的是1.12.4版本<script src="./static/webuploader-0.1.5/jQuery1.12.4.js"></script>
方法2:模块化引入
下载webuplodernpm install webuploader --save
下载jquery
npm install jquery@1.12.4
在App.vue中引入webuploder和jquery
import $ from 'jquery' import WebUploader from 'webuploader'
3.实例
upload.vue
<template> <div id="uploader" class="wu-example"> <!--用来存放文件信息--> <div id="fileLilst" class="uploader-list"></div> <div class="btns"> <!-- 选择文件的按钮 --> <div id="picker">选择文件</div> <!-- 开始上传按钮 --> <button id="uploadBtn" class="btn btn-default">开始上传</button> </div> </div> </template> <script> import $ from 'jquery' import WebUploader from 'webuploader' var fileMd5; //自定义参数 文件名 var fileName, uploader; export default { name: 'upload', mounted() { this.getAllFileList() this.uploadPlan = `上传完成(${this.fileNum} / ${this.completeNum})` //监听分块上传过程中的三个时间点 WebUploader.Uploader.register({ "before-send-file": "beforeSendFile", //整个文件上传前调用 "before-send": "beforeSend", //每个分片上传前 "after-send-file": "afterSendFile", //分片上传完毕 }, { //时间点1:所有分块进行上传之前调用此函数 beforeSendFile: function (file) { //console.log("beforeSendFile"); var deferred = WebUploader.Deferred(); //1、计算文件的唯一标记,用于断点续传 (new WebUploader.Uploader()).md5File(file, 0, 2 * 1024 * 1024) .progress(function (percentage) { $('#item1').find("p.state").text("正在读取文件信息..."); }) .then(function (val) { fileMd5 = val; $('#item1').find("p.state").text("成功获取文件信息..."); //获取文件信息后进入下一步 deferred.resolve(); }); return deferred.promise(); }, //时间点2:如果有分块上传,则每个分块上传之前调用此函数 beforeSend: function (block) { console.log("beforeSend111111"); console.log(block.chunk); var deferred = WebUploader.Deferred(); $.ajax({ type: "POST", url: "http://58.251.218.38:8080/BIMService2/model/checkOrMerge?action=checkChunk", data: { //文件唯一标记 fileMd5: fileMd5, //当前分块下标 //chunk:block.chblocknk, chunk: block.chunk, //当前分块大小 chunkSize: block.end - block.start, userId: localStorage.getItem("userId") }, dataType: "json", success: function (response) { if (eval('(' + response.ifExist + ')')) { //console.log("分片存在:" + block.chunk) //分块存在,跳过 deferred.reject(); } else { //console.log("分片不存在:" + block.chunk) //分块不存在或不完整,重新发送该分块内容 deferred.resolve(); } } }); this.owner.options.formData.fileMd5 = fileMd5; console.log("继续执行") //deferred.resolve(); return deferred.promise(); }, //时间点3:所有分块上传成功后调用此函数 afterSendFile: function (file) { //console.log("afterSendFile"); fileName = file.name; //为自定义参数文件名赋值 //如果分块上传成功,则通知后台合并分块 $.ajax({ type: "POST", url: "http://58.251.218.38:8080/BIMService2/model/checkOrMerge?action=mergeChunks", data: { fileMd5: fileMd5, fileName: fileName, ext: file.ext, //文件扩展名 projectName: $("#project option:selected").text(), //项目名称 userId: localStorage.getItem("userId") }, success: function (response) { console.log(response) //alert("上传成功"); } }); } }); uploader = WebUploader.create({ swf: 'static/webuploader/Uploader.swf', // swf文件路径 formData: { projectName: "", userId: localStorage.getItem("userId") }, server: 'http://58.251.218.38:8080/BIMService2/model/upload', // 文件接收服务端。 pick: '#picker', // 选择文件的按钮。可选 chunked: true, //是否要分片处理大文件上传 chunkSize: 2 * 1024 * 1024, //分片上传,每片5M,默认是5M auto: true, //选择文件后是否自动上传 chunkRetry: 2, //如果某个分片由于网络问题出错,允许自动重传次数 //runtimeOrder: 'html5,flash', // 在上传当前文件时,准备好下一个文件 prepareNextFile: false, threads: 1, //threads {Boolean} [可选] [默认值:3] 上传并发数。允许同时最大上传进程数。 duplicate: false,//是否重复上传(同时选择多个一样的文件),true可以重复上传 /* accept: { title: '语音上传', extensions: 'wav,zip,rar', mimeTypes: 'audio/x-wav,.zip,.rar' } */ }); this.initWebUpload() }, methods: { initWebUpload() { //当文件被加入队列之前触发 uploader.on('beforeFileQueued', (file, data) => { //console.log('beforeFileQueued') $.ajax({ type:"POST", url:"http://58.251.218.38:8080/BIMService2/model/permissionVerification", data:{ userId: localStorage.getItem("userId") }, dataType:"json", success (response){ let msg = response.message if (msg === '请先登录账号') { this.$message.error(msg); this.$router.push({path: '/Login'}); return false; } else if (msg === '您没有使用权限,请先联系管理员获取使用权限') { this.$message.error(msg); } } }); }); // 当有文件被添加进队列的时候 uploader.on('fileQueued', (file) => { //console.log('fileQueued') this.filesList.push({ id: file.id, name: file.name, size: this.reducedUnit(file.size), percentage: 0 }) this.fileNum = this.filesList.length }); //绑定uploadBeforeSend事件来给每个独立的文件添加参数 uploader.on('uploadBeforeSend', (block, data) => { //设置data参数 //data.projectName = $("#project").find("option:selected").text(); // 将存在file对象中的md5数据携带发送过去。 }, 2); // 文件上传过程中创建进度条实时显示。 uploader.on('uploadProgress', (file, percentage) => { //console.log('uploadProgress') this.filesList.forEach(item=>{ if(file.id===item.id) { item.percentage = (percentage.toFixed(2))*100+"%" } }) }); // 文件上传成功 uploader.on( 'uploadSuccess', (file, response)=> { //console.log('uploadSuccess') //console.log(response._raw); //这里可以得到后台返回的数据 this.filesList.forEach(item=>{ if(file.id===item.id) { item.percentage = '上传成功' } }) this.completeNum++ this.getAllFileList() }); // 文件上传失败,显示上传出错 uploader.on('uploadError', (file, ret) => { //console.log('上传失败') this.filesList.forEach(item=>{ if(file.id===item.id) { item.percentage = '上传失败' } }) }); // 完成上传完 uploader.on('uploadComplete', (file) => { $('#' + file.id).find('.progress').fadeOut(); }); }, //暂停上传 pauseUpload () { uploader.cancelFile(uploader.getFiles()[0].id); }, //取消上传 removeFile (index, row) { //this.filesList.splice(this.filesList.indexOf(row.name), 1); this.filesList.splice(index, 1) }, } }
4.遇到的问题
1)WebUploader 插件的上传按钮点击无效,按F12 之后才有反应
问题:上传按钮无效,debug的时候发现(上传id元素下面插件的渲染的宽度:1px,高度:1px),下面是错误图例说明:
解决方案:
在页面加一段样式:<style> #picker div:nth-child(2){width:100%!important;height:100%!important;} </style>
如果上传按钮用的不是div 标签,改为div 标签,如
<span id="picker">
选择图片,改为<div id="picker">
选择图片。如果做了第一步但用的是span 标签的话,只有鼠标在文字下方的按钮区域才有效(有效时,鼠标移上去按钮颜色会变深,无效时按钮没反应),按F12之后,只有鼠标在文字上方的按钮区域点击才有效;
用div 的话就没这个问题,整个按钮都正常。
2)修改按钮样式<style> .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 { position: relative; display: inline-block; cursor: pointer; background: #3376ff; padding: 8px 12px; color: #fff; font-size: 14px; text-align: center; border-radius: 3px; overflow: hidden; } .webuploader-pick-hover { background: #065ffc; } </style>
参考网址:
https://blog.csdn.net/Easy_____/article/details/78250736
https://www.cnblogs.com/ocean-sky/p/7575980.html -
Vue结合webuploader实现文件分片断点续传
2019-12-09 11:15:101,本项目基于vue-cli3,在public下创建webUploader目录 2,由于我司视频兼容能力业务需求,jQuery在index.html中引入。因此jQuery的引入方式有些简陋,多多包涵。。 3,文件分片断点续传底层能力组件 <...根于公司业务,封装了底层的 文件分片断点续传底层能力组件 和 带有前端交互的UI组件
使用步骤
1,本项目基于vue-cli3,在public下创建webUploader目录
2,由于我司视频兼容能力业务需求,jQuery在index.html中引入。因此jQuery的引入方式有些简陋,多多包涵。。
3,文件分片断点续传底层能力组件
<template> <div class="upload" /> </template> <script> import { fileUpload } from '@/api/upload-file' const qs = require('querystring') const md5 = window.md5 const $ = window.$ export default { name: 'VueWebuploader', props: { // 上传文件类型 accept: { type: String, default: null }, // 上传地址 url: { required: true, type: String }, // 上传文件总大小, 默认为100G fileSizeLimit: { type: Number, default: 100 * 1024 * 1024 * 1024 }, // 上传最大数量 默认为100个 fileNumLimit: { type: Number, default: 100 }, // 大小限制 默认为100G fileSingleSizeLimit: { type: Number, default: 100 * 1024 * 1024 * 1024 }, // 上传时传给后端的参数,一般为token,key等 formData: { required: true, type: Object }, // 生成formData中文件的key,下面只是个例子,具体哪种形式和后端商议 keyGenerator: { type: Function, default(file) { const currentTime = new Date().getTime() const key = `${currentTime}.${file.name}` return key } }, multiple: { type: Boolean, default: false }, // 上传按钮{Seletor|dom} uploadButton: { required: true, type: String }, // 拖拽的容器{Selector} uploadDndButton: { type: String, default: '' } }, data() { return { uploader: null, publicOption: {}, uploadContCommon: { onUploadFileErr: null, onUploadFileSuccess: null, callbackDate: null }, chunkSize: 5 * 1024 * 1024, // 分块大小 fileMd5MarkMap: new Map(), uniqueFileNameMap: new Map() } }, mounted() { this.initWebUpload() }, methods: { initWebUpload() { const that = this new WebUploader.Uploader.register( { 'before-send-file': 'beforeSendFile', 'before-send': 'beforeSend', 'after-send-file': 'afterSendFile' }, { // // 上传文件开始前触发MD5校验,用了这个会很慢很慢,因此注掉 // beforeSendFile: function(file) { // if (that.publicOption.uploadProgress) { // that.publicOption.beforeSendFile(file) // } // console.log('上传文件前触发') // // return false // // 秒传验证 // // const task = new $.Deferred() // that.uploader.md5File(file).progress(percentage => { // // 及时显示进度 // console.log('percentage', percentage) // }).then(function(val) { // // 完成 // console.log('上传文件前触发完成', val) // // $('#' + file.id + ' .operate .del_btn').show() // // $("#" + file.id + " .operate .pause_btn").show(); // // console.log("总耗时: "+ ((new Date().getTime()) - start)/ 1E3); // // md5Mark = val // that.fileMd5MarkMap.set(file.id, val) // fileUpload(qs.stringify({ status: 'md5Check', md5: val }), that.url).then(res => { // const { data } = res // if (data.ifExist) { // that.uploader.skipFile(file) // file.path = res.path // that.UploadComlate(file, this.fileMd5MarkMap.get(file.id)) // } // }) // }) // }, // 开始上传分片触发 beforeSend: function(block) { // 分片验证是否已传过,用于断点续传 var task = new $.Deferred() var file = block.file var id = file.id that.uploader.md5File(file, block.start, block.end).then(val => { var params = { status: 'chunkCheck', name: that.uniqueFileNameMap.get(id), chunkIndex: block.chunk, size: block.end - block.start, userId: that.formData.userId, companyId: that.formData.companyId, appCode: that.formData.appCode, blockmd5: val } fileUpload(qs.stringify(params), that.url).then(res => { const { data } = res if (data.ifExist === 1) { // 若存在,返回失败给WebUploader,表明该分块不需要上传 task.reject() } else { task.resolve() } }, function(jqXHR, textStatus, errorThrown) { // 任何形式的验证失败,都触发重新上传 task.reject() }) }) return $.when(task) }, // 上传文件完成触发 afterSendFile: function(file) { var chunksTotal = Math.ceil(file.size / that.chunkSize) // 合并请求 var task = new $.Deferred() var params = { status: 'chunksMerge', name: that.uniqueFileNameMap.get(file.id), chunks: chunksTotal, ext: file.ext, md5: that.fileMd5MarkMap.get(file.id), userId: that.formData.userId, companyId: that.formData.companyId, appCode: that.formData.appCode } fileUpload(qs.stringify(params), that.url).then(res => { const { data } = res if (data.path) { file.path = data.path // 最初的时候是在uploadSuccess发布的成功事件,后来发现有时取不到返回的file.path,因此换到了在merge文件碎片回调中发布上传成功事件 that.$emit('success', file, res) } task.resolve() that.uniqueFileNameMap.delete(file.id) that.fileMd5MarkMap.delete(file.id) }).catch(err => { console.info(err) }) return $.when(task) } }) this.uploader = WebUploader.create({ auto: true, // {Boolean} [可选] [默认值:false] 设置为 true 后,不需要手动调用上传,有文件选择即开始上传(选完文件后,是否自动上传) swf: '/public/webUploader/js/Uploader.swf', // swf文件路径 server: this.url + 'fileUpload', // 文件接收服务端 pick: { // 指定选择文件的按钮容器,不指定则不创建按钮 id: this.uploadButton, // {Seletor|dom} 指定选择文件的按钮容器,不指定则不创建按钮。注意 这里虽然写的是 id, 但是不是只支持 id, 还支持 class, 或者 dom 节点。 multiple: this.multiple, // {Boolean} 是否开启同时选择多个文件能力。 label: '', // {String} 请采用 innerHTML 代替 innerHTML: '' // {String} 指定按钮文字。不指定时优先从指定的容器中看是否自带文字 }, dnd: this.uploadDndButton, // {Selector} [可选] [默认值:undefined] 指定Drag And Drop拖拽的容器,如果不指定,则不启动。 disableWidgets: 'log', // {String, Array} [可选] [默认值:undefined] 默认所有 Uploader.register 了的 widget 都会被加载,如果禁用某一部分,请通过此 option 指定黑名单。 paste: document.body, // {Selector} [可选] [默认值:undefined] 指定监听paste事件的容器,如果不指定,不启用此功能。此功能为通过粘贴来添加截屏的图片。建议设置为document.body. disableGlobalDnd: true, // {Selector} [可选] [默认值:false] 是否禁掉整个页面的拖拽功能,如果不禁用,图片拖进来的时候会默认被浏览器打开。 thumb: { // {Object} [可选] 配置生成缩略图的选项。 width: 100, height: 100, quality: 100, // // 图片质量,只有type为`image/jpeg`的时候才有效。 allowMagnify: false, // // 是否允许放大,如果想要生成小图的时候不失真,此选项应该设置为false. crop: true // 是否允许裁剪。 // type: 'image/jpeg' // 为空的话则保留原有图片格式,否则强制转换成指定的类型。 }, compress: false, // {Object} [可选] 配置压缩的图片的选项。如果此选项为false, 则图片在上传前不进行压缩。 prepareNextFile: true, // {Boolean} [可选] [默认值:false] 是否允许在文件传输时提前把下一个文件准备好。 对于一个文件的准备工作比较耗时,比如图片压缩,md5序列化。 如果能提前在当前文件传输期处理,可以节省总体耗时。 chunked: true, // {Boolean} [可选] [默认值:false] 是否要分片处理大文件上传。 chunkSize: this.chunkSize, // {Boolean} [可选] [默认值:5242880] 如果要分片,分多大一片? 默认大小为5M. threads: 5, // {Boolean} [可选] [默认值:3] 上传并发数。允许同时最大上传进程数。 formData: this.formData, // {Object} [可选] [默认值:{}] 文件上传请求的参数表,每次发送都会发送此对象中的参数。 fileSizeLimit: this.fileSizeLimit, // {int} [可选] [默认值:undefined] 验证文件总大小是否超出限制, 超出则不允许加入队列。 fileSingleSizeLimit: this.fileSingleSizeLimit, // {int} [可选] [默认值:undefined] 验证单个文件大小是否超出限制, 超出则不允许加入队列。 fileNumLimit: this.fileNumLimit, // {int} [可选] [默认值:undefined] 限制上传个数,验证文件总数量, 超出则不允许加入队列。 resize: false, // 不压缩image, 默认如果是jpeg,文件上传前会压缩一把再上传! accept: this.getAccept(this.accept), // {Arroy} [可选] [默认值:null] 指定接受哪些类型的文件。 由于目前还有ext转mimeType表,所以这里需要分开指定。 duplicate: true // {Boolean} [可选] [默认值:undefined] 去重, 根据文件名字、文件大小和最后修改时间来生成hash Key. }) // 当有文件被添加进队列的时候,添加到页面预览 this.uploader.on('fileQueued', (file) => { const that = this // 将事件绑定到文件,保证在发生页面切换时之前已经开始上传的文件的回调事件不会发生变化 file.onUploadFileErr = that.uploadContCommon.onUploadFileErr file.onUploadFileSuccess = that.uploadContCommon.onUploadFileSuccess file.callbackDate = that.uploadContCommon.callbackDate that.uploader.sort(function(a, b) { return a.size - b.size })// 多文件同时加入时小文件优先上传 var uniqueFileName = md5(file.name + file.size + file.type + that.formData.userId)// 文件唯一标识 that.uniqueFileNameMap.set(file.id, uniqueFileName) that.uploader.makeThumb(file, function(error, ret) { if (error) { // console.log('预览错误,上传的不是图片吧?') } else { // console.log('预览成功 base64') } }) that.$emit('fileChange', file) }) this.uploader.on('uploadStart', (file) => { // 在这里可以准备好formData的数据 // this.uploader.options.formData.key = this.keyGenerator(file); }) // 文件上传过程中创建进度条实时显示。 this.uploader.on('uploadProgress', (file, percentage) => { this.$emit('progress', file, percentage) }) this.uploader.on('uploadSuccess', (file, response) => { // this.$emit('success', file, response) // 最初的时候是在此处发布的成功事件,后来发现有时取不到返回的file.path,因此换到了在merge文件碎片回调中发布上传成功事件 }) this.uploader.on('uploadError', (file, reason) => { this.$emit('uploadError', file, reason) }) this.uploader.on('error', (type) => { let errorMessage = '' if (type === 'F_EXCEED_SIZE') { errorMessage = `文件大小不能超过${this.fileSingleSizeLimit / (1024 * 1000)}M` } else if (type === 'Q_EXCEED_NUM_LIMIT') { errorMessage = '文件上传已达到最大上限数' } else { errorMessage = `上传出错!请检查后重新上传!错误代码${type}` } this.$emit('error', errorMessage) }) this.uploader.on('uploadComplete', (file, response) => { this.$emit('complete', file, response) }) }, upload(file) { this.uploader.upload(file) }, stop(file) { this.uploader.stop(file) }, // 取消并中断文件上传 cancelFile(file) { this.uploader.cancelFile(file) }, // 在队列中移除文件 removeFile(file, bool) { this.uploader.removeFile(file, bool) }, // 获取上传文件类型 getAccept(accept) { switch (accept) { case 'text': return { title: 'Texts', exteensions: 'doc,docx,xls,xlsx,ppt,pptx,pdf,txt', // {String} 允许的文件后缀,不带点,多个用逗号分割。 mimeTypes: '.doc,docx,.xls,.xlsx,.ppt,.pptx,.pdf,.txt' // {String} 多个用逗号分割。 } case 'video': return { title: 'Videos', exteensions: 'mp4', mimeTypes: '.mp4' } case 'image': return { title: 'Images', exteensions: 'gif,jpg,jpeg,bmp,png', mimeTypes: '.gif,.jpg,.jpeg,.bmp,.png' } default: return accept } } } } </script> <style lang="scss"> .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 { position: relative; display: inline-block; cursor: pointer; background: #00b7ee; padding: 10px 15px; text-align: center; border-radius: 3px; overflow: hidden; } .webuploader-pick-hover { background: #00a2d4; } .webuploader-pick-disable { opacity: 0.6; pointer-events:none; } </style>
4,文件分片断点续传UI组件
<!--/**--> <!--* @Author: YanHuaKang--> <!--* @Date: 2019/11/21--> <!--* @Description: 文件分片断点续传UI组件--> <!--* @remarks:--> <!--*/--> <template> <transition :name="transitionName"> <div id="mainFileUpload" :style="visibility"> <div id="header" class="clearfix"> <p class="fl title">上传资料</p> <div class="fr icons"> <span v-if="!isMinimality" @click="minimality" ><i class="minimality iconfont iconjianhao" /></span> <span v-if="isMinimality" @click="open" ><i class="spread iconfont iconjiahao" /></span> <span @click="close"><i class="close iconfont iconguanbi" /></span> </div> </div> <div class="content"> <div id="filePicker"> <div class="drag-upload"> <i class="iconfont iconshangchuan" /> <p>可拖拽文件至此直接上传</p> </div> </div> <div id="file-panel"> <div id="file-list"> <el-scrollbar wrap-class="scrollbar-wrapper"> <ul v-for="(file, index) in fileList" :key="file.id" class="file-item" :class="`file-${file.id}`" > <li class="file-type" :icon="fileCategory(file.ext)"> <svg v-if="fileCategory(file.ext) === 'image'" class="icon" aria-hidden="true"> <use xlink:href="#icontupian" /> </svg> <svg v-else-if="fileCategory(file.ext) === 'video'" class="icon" aria-hidden="true"> <use xlink:href="#iconvideo-" /> </svg> <svg v-else-if="fileCategory(file.ext) === 'audio'" class="icon" aria-hidden="true"> <use xlink:href="#iconaudio" /> </svg> <svg v-else-if="fileCategory(file.ext) === 'text'" class="icon" aria-hidden="true"> <use xlink:href="#icontext" /> </svg> <svg v-else-if="fileCategory(file.ext) === 'excel'" class="icon" aria-hidden="true"> <use xlink:href="#iconex" /> </svg> <svg v-else-if="fileCategory(file.ext) === 'ppt'" class="icon" aria-hidden="true"> <use xlink:href="#iconppt" /> </svg> <svg v-else-if="fileCategory(file.ext) === 'pdf'" class="icon" aria-hidden="true"> <use xlink:href="#iconpdf1" /> </svg> <svg v-else-if="fileCategory(file.ext) === 'compressed'" class="icon" aria-hidden="true"> <use xlink:href="#iconyasuobao" /> </svg> <svg v-else class="icon" aria-hidden="true"> <use xlink:href="#iconyemian" /> </svg> </li> <el-tooltip class="file-name-tip" effect="dark" :content="file.name" placement="top-start" > <li class="file-name singleLineOmission">{{ file.name }}</li> </el-tooltip> <li class="progress"> <el-progress :key="index" :percentage="progress[file.id]" :color="customColors" /> </li> <li class="file-size">{{ fileSize(file.size) }}</li> <!--<li class="file-status">上传中...</li>--> <li class="file-operate"> <!--<a title="开始" @click="resume(file)"><i class="iconfont iconkaishi" /></a>--> <!--<a title="暂停" @click="stop(file)"><i class="iconfont iconzanting1" /></a>--> <span class="pointer file-remove" title="移除" @click="remove(file)" ><i class=" iconfont iconguanbi" /></span> </li> </ul> <div v-if="!fileList.length" class="no-file"> <i class="iconfont icon-empty-file" /> 暂无待上传文件 </div> </el-scrollbar> </div> </div> </div> <div class="clearfix btnGroup"> <div id="btnGroup" class="fr"> <el-button type="primary" @click="clear()">清空上传</el-button> </div> </div> <vue-webuploader ref="uploader" upload-button="#filePicker" upload-dnd-button="#filePicker" multiple :form-data="formData" :url="url" @fileChange="fileChange" @progress="onProgress" @success="onSuccess" /> </div> </transition> </template> <script> import store from '@/store' import { mapGetters } from 'vuex' import VueWebuploader from '@/components/VueWebuploader/VueWebuploader.vue' import { deskAddFile, knowledgeCreateFile } from '@/api/upload-file' const WebUploader = window.WebUploader export default { name: 'FileUploader', components: { VueWebuploader }, props: { customStyle: { type: Object, default: function() { return { right: '50px', bottom: '0px' } } }, transitionName: { type: String, default: 'fade' } }, data() { return { uploadButton: 'filePicker', fileList: [], // 上传列表 url: this.$store.state.user.applicationInfo.uploadUrl, // 租户上传路径 formData: { // 参数 userId: this.$store.state.user.applicationInfo.userId, companyId: this.$store.state.user.applicationInfo.companyId, appCode: this.$store.state.user.applicationInfo.appCode }, fileQueued: new Map(), // 上传队列 customColors: [ { color: '#f56c6c', percentage: 20 }, { color: '#e6a23c', percentage: 40 }, { color: '#5cb87a', percentage: 60 }, { color: '#1989fa', percentage: 80 }, { color: '#20c7b3', percentage: 100 } ], progress: {} // 进度 } }, computed: { ...mapGetters(['visibility', 'isMinimality', 'belongs']) }, watch: {}, mounted() { store.dispatch('fileUpload/isVisibility', 2) }, created() {}, methods: { // 关闭 close() { store.dispatch('fileUpload/isVisibility', 2) }, // 最小化 minimality() { store.dispatch('fileUpload/isVisibility', 3) }, // 展开 open() { store.dispatch('fileUpload/isVisibility', 1) }, // 上传变化 fileChange(file) { if (!file.size) return file.progress = 10 this.fileList.push(file) this.$set(this.progress, file.id, 0) const sourceUid = file.source.uid // 文件上传文件唯一id const belongs = { data_type: this.belongs.data_type, ownerId: this.belongs.ownerId, parentId: this.belongs.parentId, selectCompanyId: this.belongs.selectCompanyId } this.fileQueued.set(sourceUid, belongs) }, // 上传进度 onProgress(file, percent) { const progress = (Math.ceil(percent * 100 * 100) / 100) this.$set(this.progress, file.id, progress) }, // 上传成功 onSuccess(file, response) { // console.log('file', file) // console.log('response', response) // console.log('上传成功', file) // console.log(this.belongs) // console.log('file.path', file.path) file.path = file.path || '' const fileId = file.path.split('ZmlsZUlk=')[1] const sourceUid = file.source.uid // 文件上传文件唯一id var currentFile = this.fileQueued.get(sourceUid) // console.log('sourceUid', sourceUid) // console.log('currentFile', currentFile) // console.log('this.fileQueued', this.fileQueued) // console.log('fileId', fileId) if (currentFile.data_type === 3) { const params = { fileFormat: file.ext, fileName: file.name, fileSize: file.size, fileUrl: file.path, resourceFileId: fileId } deskAddFile(params).then(res => { console.log(res) this.$message.success('上传成功!') store.dispatch('fileUpload/deskAddFileSuccess', res.data) }) } else { // const currentFile = this.fileQueued.get(sourceUid) const params = { format: file.ext, name: file.name, size: file.size, url: file.path, data_type: currentFile.data_type, ownerId: currentFile.ownerId, parentId: currentFile.parentId, selectCompanyId: currentFile.selectCompanyId, fileId: fileId } knowledgeCreateFile(params).then(res => { // console.log(res) this.$message.success('上传成功!') store.dispatch('fileUpload/createFileSuccess', res.data) }) } }, // 开始 resume(file) { this.$refs.uploader.upload(file) }, // 暂停 stop(file) { this.$refs.uploader.stop(file) }, // 移除 remove(file) { this.$confirm('确定要删除此上传任务吗?', '删除任务', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }) .then(() => { // 取消并中断文件上传 this.$refs.uploader.cancelFile(file) // 在队列中移除文件 this.$refs.uploader.removeFile(file, true) // 在ui上移除 const index = this.fileList.findIndex(ele => ele.id === file.id) this.fileList.splice(index, 1) }) .catch(() => {}) }, // 清空 clear() { var that = this if (this.fileList.length) { this.$confirm('确定要清空上传任务吗?', '清空上传', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }) .then(() => { for ( var index = 0, len = that.fileList.length; index < len; index++ ) { var file = that.fileList[index] that.$refs.uploader.cancelFile(file) that.$refs.uploader.removeFile(file, true) that.fileList.splice(index--, 1) } }) .catch(() => {}) } }, // 文件大小 fileSize(size) { return WebUploader.Base.formatSize(size) }, // 文件类别 fileCategory(ext) { let type = '' const typeMap = { image: ['gif', 'jpg', 'jpeg', 'png', 'bmp', 'webp'], video: ['mp4', 'm3u8', 'rmvb', 'avi', 'swf', '3gp', 'mkv', 'flv'], audio: ['mp3', 'wma', 'rm', 'wav', 'midi', 'ape', 'flac'], text: [ 'doc', 'txt', 'docx', 'pages', 'epub' ], excel: ['numbers', 'csv', 'xls', 'xlsx'], ppt: ['keynote', 'ppt', 'pptx'], compressed: ['zip', 'rar'], pdf: ['pdf'] } Object.keys(typeMap).forEach(_type => { const extensions = typeMap[_type] if (extensions.indexOf(ext) > -1) { type = _type } }) return type } } } </script> <style lang="scss" scoped> @import "~@/styles/theme.scss"; #mainFileUpload { visibility: hidden; width: 38%; height: 60vh; position: fixed; bottom: -522px; right: 20px; z-index: 100; border: 1px solid #e0e0e0; border-radius: 5px; background: #ffffff; overflow: hidden; transition: bottom 1s; > #header { height: 40px; line-height: 40px; padding: 0 20px; background-color: $themeColor; > .title { color: #ffffff; margin: 0; font-size: 14px; } > .fr.icons span { width: 14px; height: 14px; background-color: #ffffff; color: $themeColor; cursor: pointer; } > .fr.icons span i { font-size: 14px; } } > .content { padding: 1.5vh 20px; > #filePicker { width: 100%; height: 15vh; background-color: #fafafa; border-radius: 5px; } } .drag-upload { width: 200px; margin: 0 auto; margin-top: 18px; color: #bbbbbb; > .iconfont { font-size: 50px; } } #file-panel { margin-top: 1.5vh; } #file-panel .el-scrollbar { height: 29vh; } #file-list { position: relative; overflow-y: auto; } .file-item { position: relative; height: 30px; line-height: 30px; padding: 0 10px; margin-bottom: 10px; border-bottom: 1px solid #ccc; background-color: #fff; z-index: 1; color: #575757; > li { display: inline-block; } > .file-type { width: 24px; height: 30px; vertical-align: top; > i { display: table-cell; } } > .file-name { width: 18%; vertical-align: top; } > .progress { width: 70%; > /deep/ .el-progress { width: 100%; height: 30px; line-height: 30px; display: table; } } > .file-size { width: 80px; position: absolute; left: 0px; right: 0px; bottom: 0px; top: 0px; margin: 0 auto; font-size: 14px; text-align: center; } } .file-remove { display: table-cell; } .no-file { text-align: center; color: #575757; font-size: 14px; } } .btnGroup { margin-right: 20px; } .file-item .progress /deep/ .el-progress-bar { width: 95%; } #filePicker /deep/ .webuploader-pick { width: 100% !important; height: 100% !important; background-color: #fafafa !important; } #filePicker /deep/ .webuploader-pick-hover { background-color: #fafafa !important; } #mainFileUpload > #header > .fr.icons span { padding: 0 1px; border-radius: 2px; } </style>
5,效果图
-
vue获取上传进度_vue,webuploader实现文件分片上传,并显示上传进度
2021-01-17 19:24:58上传文件时,如果使用普通上传,则需要上传一个文件完成后才能上传下一个文件,如果文件很大时,可能会造成浏览器无响应,如果采用分片上传方式,将一个大文件分割成多块,并发上传,极大地提高大文件的上传速度。...1.效果图
2.上传文件时,如果使用普通上传,则需要上传一个文件完成后才能上传下一个文件,如果文件很大时,可能会造成浏览器无响应,如果采用分片上传方式,将一个大文件分割成多块,并发上传,极大地提高大文件的上传速度。
当网络问题导致传输错误时,只需要重传出错分片,而不是整个文件。另外分片传输能够更加实时的跟踪上传进度。
在项目中引入webuploader
第一步:引入第三方js和css
注意:也可通过安装依赖的方式,引入jquery插件
npm install jquery --save
第二步:封装Vue组件
export default{
name: 'vue-upload',
props: {
accept: {
type: Object,
default: null,
},
// 上传地址
url: {
type: String,
default: uploadUrl,
},
// 上传最大数量 默认为100
fileNumLimit: {
type: Number,
default: 1,
},
// 大小限制 默认2M
fileSingleSizeLimit: {
type: Number,
default: 1024*1024*1024*10,
},
// 上传时传给后端的参数,一般为token,key等
formData: {
type: Object,
default: null
},
// 生成formData中文件的key,下面只是个例子,具体哪种形式和后端商议
keyGenerator: {
type: Function,
default(file) {
constcurrentTime = newDate().getTime();
constkey = `${currentTime}.${file.name}`;
returnkey;
},
},
multiple: {
type: Boolean,
default: false,
},
// 上传按钮ID
uploadButton: {
type: String,
default: '',
},
},
data() {
return{
uploader: null
};
},
mounted() {
this.initWebUpload();
},
methods: {
initWebUpload() {
this.uploader = WebUploader.create({
auto: false, // 选完文件后,是否自动上传
// swf: '/static/lib/webuploader/Uploader.swf', // swf文件路径
server: this.url, // 文件接收服务端
pick: {
id: this.uploadButton, // 选择文件的按钮
multiple: this.multiple, // 是否多文件上传 默认false
label: '',
},
accept: this.getAccept(), // 允许选择文件格式。
threads:10,
fileNumLimit: this.fileNumLimit, // 限制上传个数
fileSingleSizeLimit: this.fileSingleSizeLimit, // 限制单个上传图片的大小
formData: this.formData, // 上传所需参数
chunked: true, //分片上传
chunkSize: 1024*1024*10, //分片大小
duplicate: true, // 重复上传
});
// 当有文件被添加进队列的时候,添加到页面预览
this.uploader.on('fileQueued', (file) => {
this.$emit('fileChange', file);
});
this.uploader.on('beforeFileQueued', (file) => {
this.$emit('beforeFileQueued', file);
});
this.uploader.on('uploadStart', (file) => {
console.log( this.keyGenerator)
// 在这里可以准备好formData的数据
// this.uploader.options.formData.key = this.keyGenerator(file);
});
// 文件上传过程中创建进度条实时显示。
this.uploader.on('uploadProgress', (file, percentage) => {
});
this.uploader.on('uploadSuccess', (file, response) => {
var_this=this;
this.$emit('success', file, response);
});
this.uploader.on('uploadError', (file, reason) => {
console.log(reason);
this.$emit('uploadsError', file, reason);
for(vari = 0; i < this.uploader.getFiles().length; i++) {
this.uploader.removeFile(this.uploader.getFiles()[i]);
}
this.uploader.reset();
this.initWebUpload();//初始化
});
this.uploader.on('error', (type) => {
let errorMessage = '';
if(type === 'F_EXCEED_SIZE') {
errorMessage = `文件大小不能超过${this.fileSingleSizeLimit / (1024 * 1000)}M`;
} else if(type === 'Q_EXCEED_NUM_LIMIT') {
errorMessage = '文件上传已达到最大上限数';
} else{
errorMessage = `上传出错!请检查后重新上传!错误代码${type}`;
}
console.error(errorMessage);
this.$emit('error', errorMessage);
});
this.uploader.on('uploadComplete', (file, response) => {
this.$emit('complete', file, response);
});
this.uploader.on('stopUpload', () => {
var_this=this;
_this.uploader.reset();
_this.initWebUpload();//初始化
});
this.uploader.on('uploadFinished', () => {
console.log(_this.uploader.getFiles())
});
},
getStats:function(){
return this.uploader.getStats();
},
upload(file,data) {
this.uploader.options.formData=data;//传参数
this.uploader.upload(file,data);
},
stop(file) {
this.uploader.stop(file);
},
reset(){
this.uploader.reset();
},
refresh(){
this.uploader.refresh();
},
// 取消并中断文件上传
cancelFile(file) {
this.uploader.cancelFile(file);
},
// 在队列中移除文件
removeFile(file, bool) {
this.uploader.removeFile(file, bool);
},
getFiles() {
return this.uploader.getFiles();
},
getAccept() {
return{
title: 'Videos',
extensions: 'mp4,flv,avi,wmv,ogg,rmvb,mts',
mimeTypes: '.mp4,.flv,.avi,.wmv,.ogg,.rmvb,.mts'
};
},
},
};
第三步 把第二步组件引到你的页面中,使用组件,实现分片上传
-
在Vue中使用WebUploader时一次上传触发多个上传操作问题
2020-09-29 17:26:58在Vue中将使用WebUploader实现的上传功能封装成一个单独的上传组件,但是在多个页面使用上传组件的时候,在页面之前跳转(非刷新)会导致多次注册WebUploader,最终出现一次上传触发多个上传操作。 解决方法: 在...问题描述:
在Vue中将使用WebUploader实现的上传功能封装成一个单独的上传组件,但是在多个页面使用上传组件的时候,在页面之前跳转(非刷新)会导致多次注册WebUploader,最终出现一次上传触发多个上传操作。
解决方法:
在每次注册的时候先做一次删除。具体代码:
api文档:
http://fex.baidu.com/webuploader/doc/index.html
-
Vue项目中使用WebUploader实现文件上传的方法
2020-10-16 16:11:50WebUploader是由 Baidu WebFE(FEX) 团队开发的一个简单的以 HTML5为主 , FLASH为辅 的现代 文件上传组件 。这篇文章主要介绍了在Vue项目中使用WebUploader实现文件上传,需要的朋友可以参考下 -
在Vue项目中使用WebUploader实现文件上传
2019-07-21 17:24:59WebUploader是由Baidu WebFE(FEX)团队开发的一个简单的以HTML5为主,FLASH为辅的现代文件上传组件。在现代的浏览器里面能充分发挥HTML5的优势,同时又不摒弃主流IE浏览器,沿用原来的FLASH运行时,兼容IE6+,iOS 6+,... -
关于vue 中 使用 webuploader 遇到的坑及解决方案,本文以分片上传为例解说,上传压缩包大小为2G左右在项目...
2019-09-27 08:06:07首先说一下vue中使用webuploader该如何引入调用 1. 肯定是安装操作, 本人用的是淘宝镜像 cnpm i webuploader -S 如果你没有安装淘宝镜像,则使用 npm i webuploader -S 2. 引入操作,在具体的vue组件中引入 ,... -
vue WebUploader 分块上传
2020-06-01 13:58:32由于项目使用的是BJUI前端框架,并没有使用框架本身的文件上传控件,而使用的基于jQuery的Uploadify文件上传组件,在项目使用的jslib项目中找到了BJUI框架集成jQuery Uploadify的部分,这部分代码封装在bjui-all.js... -
vue 文件及描述信息一起上传_在Vue项目中使用WebUploader实现文件上传
2021-01-11 19:32:01简介:WebUploader是由Baidu WebFE(FEX)团队开发的一个简单的以HTML5为主,FLASH为辅的现代文件上传组件。在现代的浏览器里面能充分发挥HTML5的优势,同时又不摒弃主流IE浏览器,沿用原来的FLASH运行时,兼容IE6+,... -
Vue2.0结合webuploader实现文件分片上传功能
2020-11-28 22:59:43Vue项目中遇到了大文件分片上传的问题,之前用过webuploader,索性就把Vue2.0与webuploader结合起来使用,封装了一个vue的上传组件,使用起来也比较舒爽。 上传就上传吧,为什么搞得那么麻烦,用分片上传? 分片与... -
Vue2.0结合webuploader实现文件分片上传
2019-07-08 21:21:00ue项目中遇到了大文件分片上传的问题,之前用过webuploader,索性就把Vue2.0与webuploader结合起来使用,封装了一个vue的上传组件,使用起来也比较舒爽。 上传就上传吧,为什么搞得那么麻烦,用分片上传? 分片... -
python 大文件分片上传_Vue2.0结合webuploader实现文件分片上传
2021-02-09 10:34:35Vue项目中遇到了大文件分片上传的问题,之前用过webuploader,索性就把Vue2.0与webuploader结合起来使用,封装了一个vue的上传组件,使用起来也比较舒爽。上传就上传吧,为什么搞得那么麻烦,用分片上传?分片与并发... -
vue移动端上传文件插件_vue移动端图片上传,可最多上传9张,使用webuploader插件...
2021-01-13 13:59:35}, methods: { init: function () { //上传图片 require("./WebUploader.js"); }, onSelectChange(e) { var sel = e.target, span = sel.nextElementSibling; sel.selectedIndex ? span.classList.remove("cc") : ...
-
前端样式相关
-
NFS 实现高可用(DRBD + heartbeat)
-
linux判断文件是否存在并删除
-
2018O奖-Forecast, Blueprint, Strategy, for EV's Future.PDF
-
Galera 高可用 MySQL 集群(PXC v5.6 + Ngin
-
Arduino开发环境搭建【基于Visual Studio Code平台】
-
Lazarus 2021年最新版下载
-
Windows系统管理
-
docker及nvidia-docker安装(离线)
-
基于Flink+Hudi构建企业亿级云上实时数据湖教程(PC、移动、小
-
机器人比赛代码.zip
-
JMETER 性能测试基础课程
-
MySQL 管理利器 mysql-utilities
-
智能停车场云平台(附vue+SpringBoot前后端项目源码)
-
当深度学习遇上图: 图神经网络的兴起!
-
C++代码规范和Doxygen根据注释自动生成手册
-
MySQL 性能优化(思路拓展及实操)
-
愿望清单:愿望清单-一个CS50x Web Capstone项目https://cs50.harvard.eduweb2020projectsfinalcapstone-源码
-
ContactMyReps:contactmyreps.com Web应用程序-源码
-
使用 Linux 平台充当 Router 路由器