vue项目中实现大文件分片上传要做什么,步骤是怎样
Admin 2022-08-11 群英技术资讯 332 次浏览
对于大文件的处理,无论是用户端还是服务端,如果一次性进行读取发送、接收都是不可取,很容易导致内存问题。所以对于大文件上传,采用切块分段上传,从上传的效率来看,利用多线程并发上传能够达到最大效率。
本文是基于 springboot + vue 实现的文件上传,本文主要介绍vue实现文件上传的步骤及代码实现,服务端(springboot)的实现步骤及实现请移步本人的另一篇文章:
springboot 大文件上传、分片上传、断点续传、秒传
本人分析上传总共分为:
直接上代码
文件上传:
import md5 from 'js-md5' //引入MD5加密 import UpApi from '@/api/common.js' import { concurrentExecution } from '@/utils/jnxh' /** * 文件分片上传 * @params file {File} 文件 * @params pieceSize {Number} 分片大小 默认3MB * @params concurrent {Number} 并发数量 默认2 * @params process {Function} 进度回调函数 * @params success {Function} 成功回调函数 * @params error {Function} 失败回调函数 */ export const uploadByPieces = ({ file, pieceSize = 3, concurrent = 3, success, process, error }) => { // 如果文件传入为空直接 return 返回 if (!file || file.length < 1) { return error('文件不能为空') } let fileMD5 = '' // 总文件列表 const chunkSize = pieceSize * 1024 * 1024 // 1MB一片 const chunkCount = Math.ceil(file.size / chunkSize) // 总片数 const chunkList = [] // 分片列表 let uploaded = [] // 已经上传的 let fileType = '' // 文件类型 // 获取md5 /*** * 获取md5 **/ const readFileMD5 = () => { // 读取视频文件的md5 fileType = file.name.substring(file.name.lastIndexOf('.') + 1, file.name.length) console.log('获取文件的MD5值') let fileRederInstance = new FileReader() console.log('file', file) fileRederInstance.readAsBinaryString(file) fileRederInstance.addEventListener('load', e => { let fileBolb = e.target.result fileMD5 = md5(fileBolb) var index = file.name.lastIndexOf('.') var tp = file.name.substring(index + 1, file.name.length) let form = new FormData() form.append('filename', file.name) form.append('identifier', fileMD5) form.append('objectType', fileType) form.append('chunkNumber', 1) UpApi.uploadChunk(form).then(res => { if (res.skipUpload) { console.log('文件已被上传') success && success(res) } else { // 判断是否是断点续传 if (res.uploaded && res.uploaded.length != 0) { uploaded = [].concat(res.uploaded) } console.log('已上传的分片:' + uploaded) // 判断是并发上传或顺序上传 if (concurrent == 1 || chunkCount == 1) { console.log('顺序上传') sequentialUplode(0) } else { console.log('并发上传') concurrentUpload() } } }).catch((e) => { console.log('文件合并错误') console.log(e) }) }) } /*** * 获取每一个分片的详情 **/ const getChunkInfo = (file, currentChunk, chunkSize) => { let start = currentChunk * chunkSize let end = Math.min(file.size, start + chunkSize) let chunk = file.slice(start, end) return { start, end, chunk } } /*** * 针对每个文件进行chunk处理 **/ const readChunkMD5 = () => { // 针对单个文件进行chunk上传 for (var i = 0; i < chunkCount; i++) { const { chunk } = getChunkInfo(file, i, chunkSize) // 判断已经上传的分片中是否包含当前分片 if (uploaded.indexOf(i + '') == -1) { uploadChunk({ chunk, currentChunk: i, chunkCount }) } } } /*** * 原始上传 **/ const uploadChunk = (chunkInfo) => { var sd = parseInt((chunkInfo.currentChunk / chunkInfo.chunkCount) * 100) console.log(sd, '进度') process(sd) console.log(chunkInfo, '分片大小') let inde = chunkInfo.currentChunk + 1 if (uploaded.indexOf(inde + '') > -1) { const { chunk } = getChunkInfo(file, chunkInfo.currentChunk + 1, chunkSize) uploadChunk({ chunk, currentChunk: inde, chunkCount }) } else { var index = file.name.lastIndexOf('.') var tp = file.name.substring(index + 1, file.name.length) // 构建上传文件的formData let fetchForm = new FormData() fetchForm.append('identifier', fileMD5) fetchForm.append('chunkNumber', chunkInfo.currentChunk + 1) fetchForm.append('chunkSize', chunkSize) fetchForm.append('currentChunkSize', chunkInfo.chunk.size) const chunkfile = new File([chunkInfo.chunk], file.name) fetchForm.append('file', chunkfile) // fetchForm.append('file', chunkInfo.chunk) fetchForm.append('filename', file.name) fetchForm.append('relativePath', file.name) fetchForm.append('totalChunks', chunkInfo.chunkCount) fetchForm.append('totalSize', file.size) fetchForm.append('objectType', tp) // 执行分片上传 let config = { headers: { 'Content-Type': 'application/json', 'Accept': '*/*' } } UpApi.uploadChunk(fetchForm, config).then(res => { if (res.code == 200) { console.log('分片上传成功') uploaded.push(chunkInfo.currentChunk + 1) // 判断是否全部上传完 if (uploaded.length == chunkInfo.chunkCount) { console.log('全部完成') success(res) process(100) } else { const { chunk } = getChunkInfo(file, chunkInfo.currentChunk + 1, chunkSize) uploadChunk({ chunk, currentChunk: chunkInfo.currentChunk + 1, chunkCount }) } } else { console.log(res.msg) } }).catch((e) => { error && error(e) }) // if (chunkInfo.currentChunk < chunkInfo.chunkCount) { // setTimeout(() => { // // }, 1000) // } } } /*** * 顺序上传 **/ const sequentialUplode = (currentChunk) => { const { chunk } = getChunkInfo(file, currentChunk, chunkSize) let chunkInfo = { chunk, currentChunk, chunkCount } var sd = parseInt((chunkInfo.currentChunk / chunkInfo.chunkCount) * 100) process(sd) console.log('当前上传分片:' + currentChunk) let inde = chunkInfo.currentChunk + 1 if (uploaded.indexOf(inde + '') > -1) { console.log('分片【' + currentChunk + '】已上传') sequentialUplode(currentChunk + 1) } else { let uploadData = createUploadData(chunkInfo) let config = { headers: { 'Content-Type': 'application/json', 'Accept': '*/*' } } // 执行分片上传 UpApi.uploadChunk(uploadData, config).then(res => { if (res.code == 200) { console.log('分片【' + currentChunk + '】上传成功') uploaded.push(chunkInfo.currentChunk + 1) // 判断是否全部上传完 if (uploaded.length == chunkInfo.chunkCount) { console.log('全部完成') success(res) process(100) } else { sequentialUplode(currentChunk + 1) } } else { console.log(res.msg) } }).catch((e) => { error && error(e) }) } } /*** * 并发上传 **/ const concurrentUpload = () => { for (var i = 0; i < chunkCount; i++) { chunkList.push(Number(i)) } console.log('需要上传的分片列表:' + chunkList) concurrentExecution(chunkList, concurrent, (curItem) => { return new Promise((resolve, reject) => { const { chunk } = getChunkInfo(file, curItem, chunkSize) let chunkInfo = { chunk, currentChunk: curItem, chunkCount } var sd = parseInt((chunkInfo.currentChunk / chunkInfo.chunkCount) * 100) process(sd) console.log('当前上传分片:' + curItem) let inde = chunkInfo.currentChunk + 1 if (uploaded.indexOf(inde + '') == -1) { // 构建上传文件的formData let uploadData = createUploadData(chunkInfo) // 请求头 let config = { headers: { 'Content-Type': 'application/json', 'Accept': '*/*' } } UpApi.uploadChunk(uploadData, config).then(res => { if (res.code == 200) { uploaded.push(chunkInfo.currentChunk + 1) console.log('已经上传完成的分片:' + uploaded) // 判断是否全部上传完 if (uploaded.length == chunkInfo.chunkCount) { success(res) process(100) } resolve() } else { reject(res) console.log(res.msg) } }).catch((e) => { reject(res) error && error(e) }) } else { console.log('分片【' + chunkInfo.currentChunk + '】已上传') resolve() } }) }).then(res => { console.log('finish', res) }) } /*** * 创建文件上传参数 **/ const createUploadData = (chunkInfo) => { let fetchForm = new FormData() fetchForm.append('identifier', fileMD5) fetchForm.append('chunkNumber', chunkInfo.currentChunk + 1) fetchForm.append('chunkSize', chunkSize) fetchForm.append('currentChunkSize', chunkInfo.chunk.size) const chunkfile = new File([chunkInfo.chunk], file.name) fetchForm.append('file', chunkfile) // fetchForm.append('file', chunkInfo.chunk) fetchForm.append('filename', file.name) fetchForm.append('relativePath', file.name) fetchForm.append('totalChunks', chunkInfo.chunkCount) fetchForm.append('totalSize', file.size) fetchForm.append('objectType', fileType) return fetchForm } readFileMD5() // 开始执行代码 }
并发控制:
/** * 并发执行 * @params list {Array} - 要迭代的数组 * @params limit {Number} - 并发数量控制数,最好小于3 * @params asyncHandle {Function} - 对`list`的每一个项的处理函数,参数为当前处理项,必须 return 一个Promise来确定是否继续进行迭代 * @return {Promise} - 返回一个 Promise 值来确认所有数据是否迭代完成 */ export function concurrentExecution(list, limit, asyncHandle) { // 递归执行 let recursion = (arr) => { // 执行方法 arr.shift() 取出并移除第一个数据 return asyncHandle(arr.shift()).then(() => { // 数组还未迭代完,递归继续进行迭代 if (arr.length !== 0) { return recursion(arr) } else { return 'finish' } }) } // 创建新的并发数组 let listCopy = [].concat(list) // 正在进行的所有并发异步操作 let asyncList = [] limit = limit > listCopy.length ? listCopy.length : limit console.log(limit) while (limit--) { asyncList.push(recursion(listCopy)) } // 所有并发异步操作都完成后,本次并发控制迭代完成 return Promise.all(asyncList) }
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
本篇文章给大家带来了关于JavaScript闭包的学习笔记,其中包括了闭包与方法栈以及闭包的作用,希望对大家有帮助。
滑块组件就是一个允许用户在有限区间内通过移动滑块来选择值的组件。滑块组件的应用是比较常见的,例如商品价格区间筛选,音量设置,滑块验证等等。这篇文章就给大家介绍一下用原生JS实现滑块组件,下面是实现效果,功能分析以及代码。
方法:1、利用index()和parent()方法来获取当前元素的行位置,语法为“元素对象.parent().index()+1;”;2、利用index()方法来获取当前元素的列位置,语法为“元素对象.index()+1;”。
经常浏览购物网站的朋友可能会看到这样的一个效果,就是添加商品到购物车的时候,会有抛物线这样的效果,那么这个具体是怎样做呢?接下来小编就带大家来了解一下。
这篇文章主要为大家详细介绍了vue+video.js实现视频播放列表,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
成为群英会员,开启智能安全云计算之旅
立即注册Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008