赞
踩
封装一个js文件 /* * @Descripttion: * @version: * @Author: xumingcai * @Date: 2020-04-15 17:58:03 * @LastEditors: xumingcai * @LastEditTime: 2020-04-20 16:22:45 */ import SparkMD5 from 'spark-md5'; import axios, { CancelToken, Cancel} from 'axios'; import { getToken } from '@/core/auth.js'; /** * 默认配置 */ var DEFAULT_OPTIONS = { url: '', // 上传地址 chunkSize: 2 * 1024 * 1024, // 分块大小 retryCount: 5, // 上传失败重试次数 }; /** * 状态集合 */ export var STATE = { PREPARE: 0, // 预备上传 HASH_CALC_ING: 1001, // hash值计算中 HASH_CALC_FAILED: 1002, // hash值计算失败 HASH_CALC_SUCCESS: 1003, // hash值计算成功 UPLOAD_ING: 2001, // 上传中 UPLOAD_CANCEL: 2002, // 已取消上传 UPLOAD_STOP: 2003, // 上传停止 UPLOAD_FAILED: 2004, // 上传失败 UPLOAD_SUCCESS: 2005, // 上传成功 }; /** * 事件类型 */ var EVENT_TYPES = { ERROR: 'error' }; /** * 获取字符串 * @param {*} o 非字符串 */ var toString = o => Object.prototype.toString.call(o); /** * 对象数组转数组 * @param {Object} o 对象数组o */ var toArray = o => [].slice.call(o, 0); /** * 获取文件md5加密 * @param {*} file 文件 */ export function getFileMd5 (file) { if (!file) return Promise.reject(new Error('文件不存在')); const blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice; const chunkSize = 2097152; const chunks = Math.ceil(file.size / chunkSize); let currentChunk = 0; const spark = new SparkMD5.ArrayBuffer(); const fileReader = new FileReader(); return new Promise(function (resolve, reject) { function loadNext () { var start = currentChunk * chunkSize; var end = start + chunkSize >= file.size ? file.size : start + chunkSize; fileReader.readAsArrayBuffer(blobSlice.call(file, start, end)); } fileReader.onload = function (e) { spark.append(e.target.result); currentChunk += 1; if (currentChunk < chunks) { loadNext(); } else { resolve(spark.end()); } }; fileReader.onerror = function (error) { reject(error); }; loadNext(); }); } /** * 分片上传异常类 * @param {string} message 异常信息 */ export function ShardUploadError (message, code, stack) { this.name = 'ShardUploadError'; this.code = code || 1; this.message = message || '上传失败'; this.stack = stack || (new Error()).stack; } // 继承 ShardUploadError.prototype = Object.create(Error.prototype); ShardUploadError.prototype.constructor = ShardUploadError; // 继承异常code Object.assign(ShardUploadError, { FILE_HASH_CALC_FAILED: 1002, // 文件hash值计算异常 FILE_CHUNK_UPLOAD_FAILED: 3001, // 文件块上传失败 FILE_UPLOAD_FAILED: 2004, // 上传失败 FILE_UPLOAD_CORRUPTION: -303, // 文件上传损坏 FILE_SPLICING_FAILED: -302, // 服务器文件上传成功拼接失败 PARAMETER_ERROR: 4001, // 参数错误 CANCEL_FAILED: 5001, // 取消失败 }); /** * 处理参数为正常 * @param {number} value 参数值 * @param {number} defaultValue 默认值 */ function normalizeInteger (value, defaultValue) { value = parseInt(value); if (isNaN(value)) { // 不是数字 value = defaultValue; } return value; } /** * 分片上传类 * @param {File} file 文件对象 * @param {Object} options 配置 */ export function ShardUpload (file, options = {}) { // 非new创建对象 if (!(this instanceof ShardUpload)) return new ShardUpload(file, options); // 校验文件 if (!file || !(file instanceof File)) throw new ShardUploadError('请传入File对象', ShardUploadError.PARAMETER_ERROR); // 处理配置 this.originOptions = options; options = Object.assign({}, DEFAULT_OPTIONS, options); // 是否是字符串 if (typeof options.url !== 'string') options.url = toString(options.url); options.chunkSize = normalizeInteger(options.chunkSize, DEFAULT_OPTIONS.chunkSize); options.retryCount = normalizeInteger(options.retryCount, DEFAULT_OPTIONS.retryCount); this.options = options; this.file = file; this.state = STATE.PREPARE; // 初始化状态 this.promise = null; // 进行中的延迟对象 this._events = Object.create(null); // 初始化异常对象 } var C = ShardUpload.prototype; /** * 注册事件 * @param {string|Array} 事件 * @param {Function} 事件回调方法 */ C.$on = function (event, fn) { if (Array.isArray(event)) { // 绑定多个事件 for (var i = 0, l = event.length; i < l; i++) { this.$on(event, fn); } } else { (this._events[event] || (this._events[event] = [])).push(fn); } return this; } /** * 注册一次事件 * @param {string|Array} 事件 * @param {Function} 事件回调方法 */ C.$once = function (event, fn) { var self = this; function on () { self.$off(event, fn); fn.apply(self, toArray(arguments)); } on.fn = fn; this.$on(event, on); return this; } /** * 注销事件 * @param {string|Array} 事件 * @param {Function} 注销事件回调方法 */ C.$off = function (event, fn) { var args = toArray(arguments); // 无参数,注销所有事件 if (!args.length) { this._events = Object.create(null); return this; } // event是数组,注销多个事件 if (Array.isArray(event)) { for (var i = 0, l = event.length; i < l; i++) { this.$off(event, fn); } return this; } // 是否有该事件 var cbs = this._events[event]; if (!cbs) { return this; } // 只有一个参数,注销回调方法 if (args.length === 1) { this._events[event] = Object.create(null); return this; } if (fn) { // 方法是否为非; var cb; let i = cbs.length; while(i--) { cb = cbs[i]; if (cb === fn || cb.fn === fn) { // cb.fn === fn:检测单次绑定 cbs.splice(i, 1); break; } } } return this; } /** * 触发事件 * @param {string|Array} 事件 */ C.$emit = function (event) { let cbs = this._events[event]; if (cbs) { cbs = cbs.length ? toArray(cbs) : cbs; var args = toArray(arguments); for (var i = 0, l = cbs.length; i < l; i++) { cbs[i].apply(this, args.slice(1)); } } return this; } /** * 开始上传 */ C.start = function () { switch (this.state) { case STATE.HASH_CALC_FAILED: // hash值计算失败 this.state = STATE.PREPARE; // 标记为上传成功 this.promise = this.startCalcHash(); // 继续上传 break; case STATE.UPLOAD_STOP: // 上传停止 case STATE.UPLOAD_FAILED: // 上传失败(五次失败) this.state = STATE.HASH_CALC_SUCCESS; // 标记为上传成功 this.promise = this.startUpload(this.blockIndex); // 继续上传 break; default: this.promise = this.startCalcHash(); // 继续上传 break; } return this.promise; } /** * 开始计算hash值 */ C.startCalcHash = function () { // 当前是否是预备上传状态 if (this.state !== STATE.PREPARE) return Promise.reject(new Error('预备状态才能计算hash值')); this.state = STATE.HASH_CALC_ING; // 标记hash值计算中 this.$emit('start'); this.promise = new Promise((resolve, reject) => { getFileMd5(this.file).then((hash) => { // 已取消上传 if (this.state === STATE.UPLOAD_CANCEL) throw new Cancel('取消上传'); this.hash = hash; this.state = STATE.HASH_CALC_SUCCESS; // 标记hash值计算成功 resolve(hash); }).catch((error) => { this.state = STATE.HASH_CALC_FAILED; // 标记hash值计算失败 var shardUploadError = error instanceof Cancel ? error : new ShardUploadError(error.message, ShardUploadError.FILE_HASH_CALC_FAILED, error.stack); shardUploadError.stack = error.stack; this.$emit(EVENT_TYPES.ERROR, shardUploadError); reject(shardUploadError); }); }).then(() => { return this.startUpload(); }); return this.promise; } /** * 开始上传 * @param {number} startIndex 第几块开始上传 */ C.startUpload = function (startIndex = 0) { // 检验参数 startIndex = parseInt(startIndex); if (isNaN(startIndex)) startIndex = 0; if (startIndex < 0) startIndex = 0; if (this.state !== STATE.HASH_CALC_SUCCESS) return Promise.reject(new Error('hash值计算成功才能开始上传')); // 保存当前环境 var resetCount = this.options.retryCount; // 上传失败重试次数 var chunkSize = this.options.chunkSize; // 上传块大小 var file = this.file; // 保存文件 var fileSize = file.size; // 文件大小 var retryCount = resetCount; // 设置重试次数 var chunkTotal = Math.ceil(fileSize / chunkSize); // 总块数 var hash = this.hash; // 文件hash值 var url = this.options.url; // 上传文件地址 var config = { headers: { 'Content-Type': 'multipart/form-data', 'Authorization': getToken() } }; var self = this; /** * 上传块文件 * @param {File} file 文件对象 * @param {numbe} index 上传第几块 */ function fn (file, index) { var formData = new FormData(); const nextSize = Math.min((index + 1) * chunkSize, file.size); const chunkData = file.slice(index * chunkSize, nextSize); formData.append('file', chunkData); // 块文件 formData.append('fileName', file.name); // 文件名 formData.append('partSize', chunkData.size); // 文件块大小 formData.append('totalSize', file.size); // 文件大小 formData.append('md5Code', hash); // 文件hash值 formData.append('partNum', index); // 当前上传的文件块索引 formData.append('totalPartNum', chunkTotal); // 总块数 self.blockIndex = index; // 正在上传第几个块的索引 /** * 创建异常对象 * @param {string} message 错误信息 * @param {number|string} code 错误码 */ function createError (error, code) { var error = new ShardUploadError(error.message, code, error.stack); error.chunkIndex = index; // 传第几个块 error.chunkSize = chunkData.size; // 块大小 error.chunkTotal = chunkTotal; // 块总数 error.chunkFile = chunkData; // 块文件 error.fileHash = hash; // 文件hash值 return error; } return new Promise((resolve, reject) => { axios.post(url, formData, { ...config, cancelToken: new CancelToken(function executor(c) { // executor 函数接收一个 cancel 函数作为参数 // self.cancel = self.createCancelMethod(c); self._cancel = c; }) }).then(res => { var data = res.data; if (data && (data.status === 200)) { // 上传成功 if (!data.data) { retryCount = resetCount; // 重置重试次数 if (index < chunkTotal - 1) { fn(file, index + 1).then(resolve).catch(reject); } else { self.state = STATE.UPLOAD_SUCCESS; // 标记为上传成功 // 最后一个文件块上传成功 resolve(data); } self.$emit('block-success', index + 1, chunkTotal); } else { // 已上传过 resolve(data); self.state = STATE.UPLOAD_SUCCESS; // 标记为上传成功 self.$emit('block-success', chunkTotal , chunkTotal); } } else { // 失败 var status = data.status; var error = createError(data, ShardUploadError.FILE_CHUNK_UPLOAD_FAILED); switch (status) { case -303: // 文件拼接完成后计算出的md5与前端上传时计算的不一致,需要重新上传 error.code = ShardUploadError.FILE_UPLOAD_CORRUPTION; self.$emit(EVENT_TYPES.ERROR, error); self.state = STATE.UPLOAD_FAILED; // 标记为上传失败 self.blockIndex = 0; // 重新从0开始 reject(error); self.$emit('block-success', 0, chunkTotal); break; case -302: // 文件上传成功,但是文件片拼接时出错,需要重新请求以再次拼接文件片 error.code = ShardUploadError.FILE_SPLICING_FAILED; default: if (retryCount <= 1) { self.$emit(EVENT_TYPES.ERROR, error); self.state = STATE.UPLOAD_FAILED; // 标记为上传失败 reject(error); } else { retryCount -= 1; fn(file, index).then(resolve).catch(reject); } break; } self.state = STATE.UPLOAD_FAILED; // 标记为上传失败 } }).catch(response => { if (response instanceof Cancel || retryCount <= 1) { var error = response instanceof Cancel ? response : createError(response, ShardUploadError.FILE_CHUNK_UPLOAD_FAILED); self.$emit(EVENT_TYPES.ERROR, error); self.state = STATE.UPLOAD_FAILED; // 标记为上传失败 reject(error); } else { retryCount -= 1; fn(file, index).then(resolve).catch(reject); } }); }); } this.state = STATE.UPLOAD_ING; // 标记为开始上传 this.promise = fn(file, startIndex); return this.promise; } /** * # 废弃 * 创建取消方法 * @param {Function} c 取消的调用方法 */ C.createCancelMethod = function (c) { var self = this; return function () { if (self.state !== STATE.UPLOAD_ING) return Promise.reject(new ShardUploadError('上传中才能取消上传', ShardUploadError.CANCEL_FAILED)); if (!c || typeof c !== 'function') return Promise.reject(new ShardUploadError('取消方法为空或类型非方法', ShardUploadError.CANCEL_FAILED)); c.call(); // 调用取消方法 self.state = STATE.UPLOAD_CANCEL; // 标记为上传取消状态 return Promise.resolve('取消成功'); }; } C.cancel = function () { typeof this._cancel === 'function' && this._cancel('取消上传'); this.state = STATE.UPLOAD_CANCEL; // 标记为上传取消状态 return Promise.resolve('取消成功'); }
//html中的代码 import { ShardUpload } from "@/utils/shard-upload"; //把文件引入 <el-upload ref="uploader" action class="avatar-uploader" :http-request="handleVideoSuccess" :before-upload="beforeUploadVideo" :show-file-list="false" style="text-align: start;" > <video v-if="preVideoUrl !='' && !videoFlag" :src="preVideoUrl" //回显视频 class="avatar video-avatar" controls="controls" id="videopause" >您的浏览器不支持视频播放</video> <i v-else-if="preVideoUrl =='' && !videoFlag" class="el-icon-plus avatar-uploader-icon" style=" color: #8c939d; width: 30px; line-height: 30px; text-align: center; height: 30px; border: 1px dashed;" ></i> <el-progress //进度条的 v-if="videoFlag" type="circle" :percentage="videoUploadPercent" style="margin-top: 10px" ></el-progress> </el-upload>
//详细方法的用法 handleVideoSuccess(res, file) { if (this.shardUpload != null) { //重新定义 this.shardUpload.cancel(); this.shardUpload == null; } // 创建批量上传实例 // this.isShowUploadVideo = true; this.videoFlag = true; this.videoUploadPercent = 0; //进度的百分比一开始为0 this.continiures = res; this.continiufile = res.file; //上传失败的重新调用这个方法并且传相对应的值 var file = res.file; var self = this; self.shardUpload = new ShardUpload(file, { url: process.env.VUE_APP_UPLOAD_PART_URL, }); // 开始上传 self.shardUpload .start() .then((res) => { this.videoFlag = false; var videoTimes = 0; this.$message.success("上传成功"); console.log(res); this.Continue = false; if (res.data.videoInfo != "" && res.data.videoInfo != null) { videoTimes = res.data.videoInfo.duration; } this.preVideoUrl = res.data.fdfsHost + res.data.resourceUrl; //回显视频 const videoPath = res.data.resourceUrl; //const videoPath = res.data.fdfsHost + res.data.resourceUrl; const fileName = res.data.fileName; const fileType = res.data.format; var videoItem = { //视频上传的相关东西传给后端要用的 fileType: fileType, fileName: fileName, videoTimes: videoTimes, fileUrl: videoPath, }; this.addtableData.thematicFileRelationList = []; //因为只要一个视频路径 所以先清空数组 再求赋值 传给后端 this.addtableData.thematicFileRelationList.unshift(videoItem); // //取最新的上传视频路径 // this.addtableData.thematicFileRelationList = this.addtableData.thematicFileRelationList[this.addtableData.thematicFileRelationList.length -1] }) .catch((error) => { this.statusCode = error.status; this.Continue = true; //失败后出现继续上传的按钮 重新点击调用这个方法 // 失败 // this.onFaild(error); // throw error; }); // 上传进度事件 self.shardUpload.$on("block-success", function (index, total) { console.log("--------------------------------------"); console.log("第几块上传成功:" + index); console.log("总块数:" + total); console.log("进度:" + ((index / total) * 100 + "%")); self.videoUploadPercent = +((index / total) * 100).toFixed(1); //进度条的赋值 }); },
以上就是所有内容了 可能有的不完整的还需要你们发现出来 这个是在使用增加某些内容的时候使用的 比如是表单的提交 里面有视频 可以用这个 有的可能没有放上去 可以随时发问 我会第一时间告知的
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。