vue项目中实现大文件分片上传要做什么,步骤是怎样
Admin 2022-08-11 群英技术资讯 837 次浏览
在实际应用中,我们有时候会遇到“vue项目中实现大文件分片上传要做什么,步骤是怎样”这样的问题,我们该怎样来处理呢?下文给大家介绍了解决方法,希望这篇“vue项目中实现大文件分片上传要做什么,步骤是怎样”文章能帮助大家解决问题。对于大文件的处理,无论是用户端还是服务端,如果一次性进行读取发送、接收都是不可取,很容易导致内存问题。所以对于大文件上传,采用切块分段上传,从上传的效率来看,利用多线程并发上传能够达到最大效率。
本文是基于 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进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
在我开发的很多系统里面,包括Winform混合框架、Bootstrap开发框架等系列产品中,我都倾向于动态配置菜单,并管理对应角色的菜单权限和页面权限,实现系统对用户权限的控制,菜单一般包括有名称、图标、顺序、URL连接等相关信息,对于VUE+Element 前端应用来说
目录前言框选的实现Group 类的实现小结前言虽然这两个月基金涨的还行,但是离回本还有一大大大段距离????。今天呢,我们要实现的是 canvas 中物体的框选功能,大概就像下面这个样子:然后话不多说,直接开撸 ✍????框选的实现先来说下拖蓝选区(鼠标拖拽区域)的实现方式吧,仔细观察你会发现选区其实就是个普通矩形,这个区域由鼠标
本篇文章给大家带来了关于JavaScript的相关知识,reduce是数组原型对象上的一个方法,可以帮助我们操作数组。本文将和大家分享4个关于JavaScript中数组reduce的用法,希望对大家有所帮助。
本文给大家分享CocosCreator中计时器的使用,CocosCreator中计时器有setTimeOut、setInterval、Schedule。下面我们就来看看它们要怎样使用及有何不同?
本文实例讲述了JavaScript中继承原理与用法。分享给大家供大家参考,具体如下:
成为群英会员,开启智能安全云计算之旅
立即注册关注或联系群英网络
7x24小时售前:400-678-4567
7x24小时售后:0668-2555666
24小时QQ客服
群英微信公众号
CNNIC域名投诉举报处理平台
服务电话:010-58813000
服务邮箱:service@cnnic.cn
投诉与建议:0668-2555555
Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008