Vue+element-ui实现大文件分片上传,可控制同时上传的并发数
2020-06-04 16:10
288 查看
用过element-ui中Upload上传组件的都知道,他不支持文件分片上传,如果有文件分片的上传的需求时,只能去安装其他的上传组件,如vue-simple-uploader,webuploader等,我们公司的需求涉及到获取视频时长,上传文件名等等,如果换一个上传组件就要写两套获取文件信息的代码,不是很方便,查阅element-ui中Upload文档之后发现它有一个属性
设置这个属性之后,可以覆盖组件自带的上传行为,可以实现自己的自定义上传,具体实现如下:
//template <el-upload :http-request="chunkedUpload" :ref="chunkedUpload" :action="uploadUrl" :data="uploadData" :on-error="onError" :before-remove="beforeRemove" name="file">
//js部分 import chunkedUpload from './chunkedUpload' export default { data() { return { uploadData: { //这里面放额外携带的参数 }, //文件上传的路径 uploadUrl: process.env.BASE_API + '/oss/oss/uploadChunkFile', //文件上传的路径 chunkedUpload: chunkedUpload // 分片上传自定义方法,在头部引入了 } }, methods: { onError(err, file, fileList) { this.$store.getters.chunkedUploadXhr.forEach(item => { item.abort() }) this.$alert('文件上传失败,请重试', '错误', { confirmButtonText: '确定' }) }, beforeRemove(file) { // 如果正在分片上传,则取消分片上传 if (file.percentage !== 100) { this.$store.getters.chunkedUploadXhr.forEach(item => { item.abort() }) } } } }
//chunkedUpload.js import SparkMD5 from 'spark-md5' import axios from 'axios' import store from '@/store' // 如果上传错误,获取报错信息 function getError(action, option, xhr) { let msg if (xhr.response) { msg = `${xhr.response.error || xhr.response}` } else if (xhr.responseText) { msg = `${xhr.responseText}` } else { msg = `fail to post ${action} ${xhr.status}` } const err = new Error(msg) err.status = xhr.status err.method = 'post' err.url = action return err } // 上传成功完成合并之后,获取服务器返回的信息 function getBody(xhr) { const text = xhr.responseText || xhr.response if (!text) { return text } try { return JSON.parse(text) } catch (e) { return text } } // 分片上传的自定义请求,以下请求会覆盖element的默认上传行为 export default function upload(option) { if (typeof XMLHttpRequest === 'undefined') { return } const spark = new SparkMD5.ArrayBuffer()// md5的ArrayBuffer加密类 const fileReader = new FileReader()// 文件读取类 const action = option.action // 文件上传上传路径 const chunkSize = 1024 * 1024 * 30 // 单个分片大小 let md5 = ''// 文件的唯一标识 const optionFile = option.file // 需要分片的文件 let fileChunkedList = [] // 文件分片完成之后的数组 const percentage = [] // 文件上传进度的数组,单项就是一个分片的进度 // 文件开始分片,push到fileChunkedList数组中, 并用第一个分片去计算文件的md5 for (let i = 0; i < optionFile.size; i = i + chunkSize) { const tmp = optionFile.slice(i, Math.min((i + chunkSize), optionFile.size)) if (i === 0) { fileReader.readAsArrayBuffer(tmp) } fileChunkedList.push(tmp) } // 在文件读取完毕之后,开始计算文件md5,作为文件唯一标识 fileReader.onload = async(e) => { spark.append(e.target.result) md5 = spark.end() + new Date().getTime() console.log('文件md5为--------', md5) // 将fileChunkedList转成FormData对象,并加入上传时需要的数据 fileChunkedList = fileChunkedList.map((item, index) => { const formData = new FormData() if (option.data) { // 额外加入外面传入的data数据 Object.keys(option.data).forEach(key => { formData.append(key, option.data[key]) }) // 这些字段看后端需要哪些,就传哪些,也可以自己追加额外参数 formData.append(option.filename, item, option.file.name)// 文件 formData.append('chunkNumber', index + 1)// 当前文件块 formData.append('chunkSize', chunkSize)// 单个分块大小 formData.append('currentChunkSize', item.size)// 当前分块大小 formData.append('totalSize', optionFile.size)// 文件总大小 formData.append('identifier', md5)// 文件标识 formData.append('filename', option.file.name)// 文件名 formData.append('totalChunks', fileChunkedList.length)// 总块数 } return { formData: formData, index: index } }) // 更新上传进度条百分比的方法 const updataPercentage = (e) => { let loaded = 0// 当前已经上传文件的总大小 percentage.forEach(item => { loaded += item }) e.percent = loaded / optionFile.size * 100 option.onProgress(e) } // 创建队列上传任务,limit是上传并发数 function sendRequest(chunks, limit = 3) { return new Promise((resolve, reject) => { const len = chunks.length let counter = 0 let isStop = false const start = async() => { if (isStop) { return } const item = chunks.shift() console.log() if (item) { const xhr = new XMLHttpRequest() const index = item.index // 分片上传失败回调 xhr.onerror = function error(e) { isStop = true reject(e) } // 分片上传成功回调 xhr.onload = function onload() { if (xhr.status < 200 || xhr.status >= 300) { isStop = true reject(getError(action, option, xhr)) } if (counter === len - 1) { // 最后一个上传完成 resolve() } else { counter++ start() } } // 分片上传中回调 if (xhr.upload) { xhr.upload.onprogress = function progress(e) { if (e.total > 0) { e.percent = e.loaded / e.total * 100 } percentage[index] = e.loaded console.log(index) updataPercentage(e) } } xhr.open('post', action, true) if (option.withCredentials && 'withCredentials' in xhr) { xhr.withCredentials = true } const headers = option.headers || {} for (const item in headers) { if (headers.hasOwnProperty(item) && headers[item] !== null) { xhr.setRequestHeader(item, headers[item]) } } // 文件开始上传 xhr.send(item.formData) //这里是把所有分片上传的xhr存到全局中,如果用户手动取消上传,或者上传出现错误,则要调用xhr.abort()把store中所有xhr的停止,不然文件还会继续上传 store.commit('SET_CHUNKEDUPLOADXHR', xhr) } } while (limit > 0) { setTimeout(() => { start() }, Math.random() * 1000) limit -= 1 } }) } try { // 调用上传队列方法 等待所有文件上传完成 await sendRequest(fileChunkedList, 3) // 这里的参数根据自己实际情况写 const data = { identifier: md5, filename: option.file.name, totalSize: optionFile.size } // 给后端发送文件合并请求 const fileInfo = await axios({ method: 'post', url: '/api/oss/oss/mergeChunkFile', data: data }) // 这个8200是我们oss存储成功的code,根据自己实际情况可以变 if (fileInfo.data.code === 8200) { const success = getBody(fileInfo.request) option.onSuccess(success) return } } catch (error) { option.onError(error) } } }
chunkedUpload.js写的太乱了,后续有时间优化完再分享一次,可以把一些回调方法和属性设置写到外面去,这样就做到了通用性
相关文章推荐
- 关于vue+element ui 实现上传文件
- vue+elementui的table行内实现el-upload文件添加/多文件上传
- Vue 结合element -ui实现多文件上传到后台并显示进度条
- Vue Element UI + OSS实现上传文件功能
- Vue Element UI 实现文件excel的上传及下载2种方式(文件流及a标签)
- vue -element-ui 文件上传upload 组件 实现 及其后台
- Vue上传文件:ElementUI中的upload实现
- vue+elementUI实现表单和图片上传及验证功能示例
- vue+element UI实现表格中动态添加开关控制按钮
- vue+elementui el-upload上传文件携带参数
- Vue之利用Element-ui文件上传——头像上传
- vue+element-ui上传文件
- vue+elementUI实现图片上传功能
- vue+ elmentUI实现form表单中文件、图片的上传
- vue中element-ui文件拖拽上传
- vue2.0 使用element-ui里的upload组件实现多图上传。采用FORMDATA的方式上传。
- sau交流学习社区--在element-ui中新建FormData对象组合上传图片和文件的文件对象,同时需要携带其他参数
- vue + element-ui + springboot + 阿里云OSS 使用表单进行图片文件等的上传(具有上传滚动条效果)
- element-ui多文件上传的实现示例
- Spring Boot + Vue Element实现Excel文件上传、解析、下载(含完整实现过程)