时间:2021-08-27 09:58:49 | 栏目:vue | 点击:次
最近项目中需要用到百度的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>
四、运行效果图