vue webuploader 文件上传组件开发
程序员文章站
2022-04-09 21:49:14
最近项目中需要用到百度的webuploader大文件的分片上传,对接后端的fastdfs,于是着手写了这个文件上传的小插件,步骤很简单,但是其中猜到的坑也不少,详细如下:...
最近项目中需要用到百度的webuploader大文件的分片上传,对接后端的fastdfs,于是着手写了这个文件上传的小插件,步骤很简单,但是其中猜到的坑也不少,详细如下:
一、封装组件
引入百度提供的webuploader.js、uploader.swf
css样式就直接写在组件里面了
<template> <div> <div id="list" class="uploader-list"></div> <div id="wrapper"> <div class="uploader-container"> <div :id="id" limitsize="1" :ext="ext"></div> <el-button style="margin-bottom:10px;float:left;" size="small" :loading="uploadloading" type="success" @click="start">上传到服务器</el-button> <el-button style="margin-left: 20px;margin-bottom:10px;float:left;" :disabled="stopbtn" size="small" type="danger" @click="stop">暂停上传</el-button> </div> </div> <div class="el-upload__tip">{{tip}}</div> <div class="file-list"> <ul class="el-upload-list el-upload-list--text"> <li v-for="file in filelist" :class="['el-upload-list__item', 'is-' + file.status]" :key="file"> <a class="el-upload-list__item-name"> <i class="el-icon-document"></i>{{file.name}} </a> <label class="el-upload-list__item-status-label"> <i :class="{'el-icon-upload-success': true,'el-icon-circle-check': listtype === 'text', 'el-icon-check': ['picture-card', 'picture'].indexof(listtype) > -1}"></i> </label> <i class="el-icon-close" @click="removefile(file)"></i> <el-progress v-if="file.status === 'uploading'" :type="listtype === 'picture-card' ? 'circle' : 'line'" :stroke-width="listtype === 'picture-card' ? 6 : 2" :percentage="file.percentage"> </el-progress> </li> </ul> </div> </div> </template> <script> import '../js/jquery.js' import '../js/webuploader.js' import { base64 } from 'js-base64' import cryptojs from 'crypto-js'; export default{ name: 'fileupload', props: { id: { type: string, default: function(){ return "filepicker"; } }, //上传提示 tip: { type: string, default: function(){ return ""; } }, //文件后缀名限制 ext: { type: string, default: function(){ return "jpg,jpeg,png,pdf,mp4,avi.mp3"; } }, //分片大小设置 chunksize: { type: number, default: function(){ return 2097152; } }, //分片上传重试次数 chunkretry: { type: number, default: function(){ return 1; } }, //是否自动上传 auto: { type: boolean, default: function(){ return false; } }, //上传文件大小限制 sizelimit: { type: number, default: function(){ return 209715200; } }, //上传文件数量限制 countlimit: { type: number, default: function(){ return 5; } } }, data(){ return{ appid: appconfig.appid, securitykey: appconfig.securitykey, checkurl: appconfig.checkurl, uploadurl: appconfig.uploadurl, mergeurl: appconfig.mergeurl, previewname: '选择文件', wul_filemd5: '', wul_size: 0, wul_filename: '', wul_chunk: 0, wul_uploader: '', filelist: [], listtype: 'text', percentage: 0, fileobject: { uid: '', name: '', ext: '', type: '', status: '', percentage: 0, url: '' }, uploadloading: false, stopbtn: true } }, methods: { /** * 获取当前上传列表中的文件 * @returns {array|*} */ getfilelist: function(){ return this.filelist; }, //绑定事件 wul_init: function() { //提示只能选择一个文件 this.wul_uploader.on('filesqueued', function (files) { if (files.length > 1) { this.$message({ message: '请选择一张图片', type: 'error' }); for (var i = 0; i < files.length; i++) { this.wul_uploader.cancelfile(files[i]); } this.wul_uploader.reset(); this.wul_filemd5 = ""; this.wul_size = 0; this.wul_filename = ""; this.wul_chunk = 0; //当前切片数 }else{ if( this.filelist.length == this.countlimit ){ this.$message({ message: '已经达到上传文件限制数量', type: 'error' }); }else{ //此时往需要上传的文件列表中添加文件 let file = { uid: date.now() + this.tempindex++, name: files[0].name, type: files[0].type, ext: files[0].ext, status: "ready", percentage: 0 } this.fileobject = file; this.filelist.push(this.fileobject); } } }.bind(this)); //文件校验格式和大小 this.wul_uploader.on('error', function (type) { debugger if (type == 'q_exceed_size_limit') { this.$message({ message: '文件超过指定大小', type: 'error' }); } if (type == 'q_type_denied') { this.$message({ message: '文件格式错误,请选择文件', type: 'error' }); } if (type == 'f_exceed_size') { this.$message({ message: "文件超过" + this.sizelimit / 1024 / 1024 + "m", type: 'error' }); } }.bind(this)); //上传进度 this.wul_uploader.on('uploadprogress', function (file, percentage) { this.percentage = percentage * 100; this.fileobject.status = "uploading"; this.fileobject.percentage = this.percentage; console.log(this.fileobject.percentage); }.bind(this)); //每次切片上传完成之后的判断 this.wul_uploader.on('uploadaccept', function (object, ret) { if (ret.responsecode != 0) { this.wul_uploader.cancelfile(this.wul_uploader.getfiles()[0].id); } }); this.wul_uploader.on('uploadbeforesend', function(object, data, headers) { console.log(data); }); }, option: function(key, val) { this.wul_uploader.option(key, val); var options = this.wul_uploader.options; this.wul_uploader.destroy(); //注销uploader this.wul_uploader = webuploader.create(options); this.wul_init(); }, start: function(){ if(this.wul_uploader.getfiles()[0] != null) { this.wul_uploader.upload(this.wul_uploader.getfiles()[0].id); this.uploadloading = true; this.stopbtn = false; } else { this.$message({ message: "请选择上传文件", type: 'error' }); } }, stop: function(){ this.wul_uploader.cancelfile(this.wul_uploader.getfiles()[0].id); }, removefile: function(file){ this.filelist.splice(this.filelist.indexof(file), 1); }, change: function(){ this.option('accept', { title: 'images', extensions: 'gif,jpg,jpeg,bmp,png' }); } }, mounted(){ webuploader.uploader.register({ "before-send-file": "beforesendfile", "before-send": "beforesend", "after-send-file": "aftersendfile", }, { beforesendfile: function (file) { var deferred = webuploader.deferred(); this.wul_uploader.md5file(file).then(function (val) { this.wul_filemd5 = val; this.wul_size = file.size; this.wul_filename = file.name; var timestamp = date.parse(new date()) / 1000; var signparam = "{chunksize=" + this.chunksize + ", filemd5=" + this.wul_filemd5 + ", size=" + this.wul_size + ", timestamp=" + timestamp + "}"; var sign = base64.encode(cryptojs.hmacsha1(signparam, this.securitykey)); // 获取断点续传位置 jquery.ajax({ type: "post", // 测试 url: this.checkurl, data: { // 文件大小 size: this.wul_size, // 文件唯一标记 filemd5: this.wul_filemd5, // 切片大小 chunksize: this.chunksize, // 签名 sign: sign, // 应用分配id appid: this.appid, // 当前时间戳 timestamp: timestamp }, datatype: "json", // 上传失败 error: function (xmlhttprequest, textstatus, errorthrown) { this.$message({ message: "上传失败...", type: 'error' }); this.uploadloading = false; this.stopbtn = true; }.bind(this), success: function (response) { if (response.responsecode == 0) { // 切片获取成功 this.wul_chunk = response.chunk; deferred.resolve(); } else { // 切片获取失败,请求成功 this.wul_uploader.cancelfile(file); //取消文件上传 this.$message({ message: "切片检查失败,请联系管理员", type: 'error' }); deferred.resolve(); this.uploadloading = false; this.stopbtn = true; } }.bind(this) }); return deferred.promise(); }.bind(this)); return deferred.promise(); }.bind(this), beforesend: function (block) { var deferred = webuploader.deferred(); if (block.chunk < this.wul_chunk) { return deferred.reject(); } this.wul_uploader.md5file(block.blob).then(function (chunkmd5) { var timestamp = date.parse(new date()) / 1000; var signparam = '{chunk=' + block.chunk + ', chunkmd5=' + chunkmd5 + ', chunksize=' + this.chunksize + ', filemd5=' + this.wul_filemd5 + ', size=' + this.wul_size + ', timestamp=' + timestamp + '}'; var signtemp = cryptojs.hmacsha1(signparam, this.securitykey); var sign = base64.encode(signtemp); //获取sign值 this.wul_uploader.options.formdata = { 'timestamp': timestamp, 'appid': this.appid, 'chunk': block.chunk, 'chunksize': this.chunksize, 'filemd5': this.wul_filemd5, 'chunkmd5': chunkmd5, 'size': this.wul_size, 'sign': sign }; deferred.resolve(); }.bind(this)) return deferred.promise(); }.bind(this), aftersendfile: function (file) { var timestamp = date.parse(new date()) / 1000; var signparam = "{chunksize=" + this.chunksize + ", filemd5=" + this.wul_filemd5 + ", filename=" + file.name + ", size=" + this.wul_size + ", timestamp=" + timestamp + "}"; var sign = base64.encode(cryptojs.hmacsha1(signparam, this.securitykey)); // 如果分块上传成功,则通知后台合并分块 jquery.ajax({ type: "post", url: this.mergeurl, data: { appid: this.appid, filemd5: this.wul_filemd5, filename: file.name, chunksize: this.chunksize, sign: sign, size: this.wul_size, timestamp: timestamp }, success: function (response) { if (response.responsecode == 0) { this.fileobject.status = "success"; this.fileobject.percentage = 100; this.fileobject.url = response.filepath; } else { this.fileobject.status = "exception"; this.$message({ message: "上传失败,失败原因:" + response.responsemsg, type: 'error' }); } this.uploadloading = false; this.stopbtn = true; this.wul_uploader.reset(); this.wul_filemd5 = ""; this.wul_size = 0; this.wul_filename = ""; this.wul_chunk = 0; //当前切片数 }.bind(this) }); }.bind(this) }); this.wul_uploader = webuploader.create({ // swf文件路径 swf: '../js/uploader.swf', // 文件接收服务端。 server: this.uploadurl, // 定义选择按钮 pick: { "id": "#" + this.id, "innerhtml": this.previewname }, // 自动上传 auto: this.auto, // 禁止浏览器打开文件 disableglobaldnd: true, // 添加截图功能 paste: '#wrapper', // 定义拖动面板 dnd: '#wrapper', // 分片上传 chunked: true, // 分片大小为2m chunksize: this.chunksize, // 分片上传失败重试次数 chunkretry: this.chunkretry, // 图片不做压缩 compress: false, // 队列设置10个,为了选择多个文件的时候能提示 filenumlimit: 10, // 提前准备好下一个文件 preparenextfile: true, // 限制单个文件大小 filesinglesizelimit: this.sizelimit, //线程数 threads : 1, // 限制格式 accept: { title: "access", extensions: this.ext } }); this.wul_init(); } } </script> <style> /* ----------------reset css--------------------- */ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, figcaption, figure, footer, header, hgroup, menu, nav, section, summary, time, mark, audio, video, input { margin: 0; padding: 0; border: none; outline: 0; font-size: 100%; font: inherit; vertical-align: baseline; } html, body, form, fieldset, p, div, h1, h2, h3, h4, h5, h6 { -webkit-text-size-adjust: none; } article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; } body { font-family: arial, sans-serif; } ol, ul { list-style: none; } blockquote, q { quotes: none; } blockquote:before, blockquote:after, q:before, q:after { content: ''; content: none; } ins { text-decoration: none; } del { text-decoration: line-through; } table { border-collapse: collapse; border-spacing: 0; } /* ------------ */ #wrapper { width: 100%; margin: 0 auto; height: 35px; } .img-preview { width: 160px; height: 90px; margin-top: 1em; border: 1px solid #ccc; } .cropper-wraper { position: relative; } .upload-btn { background: #ffffff; border: 1px solid #cfcfcf; color: #565656; padding: 10px 18px; display: inline-block; border-radius: 3px; margin-left: 10px; cursor: pointer; font-size: 14px; position: absolute; right: 1em; bottom: 2em; } .upload-btn:hover { background: #f0f0f0; } .uploader-container{ width: 100%; font-size: 10px; } .webuploader-container { position: relative; width: 100px; height: 21px; float: left; } .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: 6px 15px; color: #fff; text-align: center; border-radius: 3px; overflow: hidden; } .webuploader-pick-hover { background: #00a2d4; } .webuploader-pick-disable { opacity: 0.6; pointer-events:none; } .file-list{ width: 100%; } </style>
二、导出组件
var fileupload = require('./src/file_upload.vue'); module.exports = { fileupload }
三、demo 引用方式
<template> <div> <el-card class="box-card"> <fileupload ref="fileupload" :ext="ext" :countlimit="5" :tip="tip">aaa</fileupload> </el-card> </div> </template> <script> import {fileupload} from '@/components/fileupload/index.js' export default{ name: 'hello', components: {fileupload}, data(){ return{ filelist: [], ext: 'png,jpg,jpeg,mp3,mp4,pdf', tip: '可上传png/jpg/jpeg/mp3/mp4/pdf,大小不超过200m' } }, created(){ }, methods: { getfilelist: function(){ this.filelist = this.$refs.fileupload.getfilelist(); console.log(this.filelist); } } } </script>
四、运行效果图
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。