vue文件批量上传及进度条展示的实现方法
第九系艾文 人气:0主要实现功能
1.多文件拖拽上传
2.显示文件上传进度条
3.上传期间,还可以继续拖拽上传
4.可以支持手动修改上传并发任务数
5.上传失败自动重试,最大重试次数3次
6.上传过程中可以手动取消上传
本次使用的是element ui ,上传拖拽是通过el-upload实现的。
<el-upload :before-upload="beforeUpload" action="#" class="upload" drag multiple > <el-icon class="el-icon--upload"> <upload-filled/> </el-icon> <div class="el-upload__text"> 拖拽或 <em>点击上传</em> </div> <template #tip> <div class="el-upload__tip"> 请上传文件,仅支持上传文件,不支持文件夹 </div> </template> </el-upload>
上传之前,触发 beforeUpload 方法,该方法进行封装调用
beforeUpload(raw) { addUploadFile(raw) return false }
上传初始化逻辑
初始化进度条等相关数据
export function addUploadFile(raw) { const upload = uploadStore() const uploadProgress = { progress: 0, file_id: '', file_name: raw.name, percent: [], speed: '0 MB', file_size: raw.size, upload_size: 0, upload_time: new Date() } // status上传状态 0 队列,1 上传中,2 上传成功 , 3 取消上传 // failTryCount 失败上传次数, 没上传一次,自动减去已,当为0的时候,停止上传 upload.multiFileList.push({file: raw, progress: uploadProgress, status: 0, failTryCount: 3}) multiUpload() }
该代码引用了一个类似vuex的存储库 Pinia
下面内容通过pinia定义了一个存储,存储有三个字段
uploadStore()
export const uploadStore = defineStore('upload', { state: () => ({ multiFileList: [], processNumber: 3, promise: [] }) })
multiUpload()
文件上传核心内容就是该方法了,主要时过滤上传状态为0,然后批量加入上传队列
export function multiUpload() { const upload = uploadStore() const readFileList = [] upload.multiFileList.forEach(res => { if (res.status === 0) { readFileList.push(res) } }) if (readFileList.length > 0) { multiRun(upload, readFileList.slice(0, upload.processNumber), uploadAsync) } }
multiRun()
该方法,就是并发上传核心逻辑,通过Promise进行批量上传
function multiRun(upload, keyList, func) { const processNumber = upload.processNumber const promise = upload.promise for (let i = 0; i < processNumber - promise.length; i++) { promise.push(Promise.resolve()) } let reduceNumber = promise.length - processNumber if (reduceNumber > 0) { upload.promise = promise.slice(0, reduceNumber) } for (let j = 0; j < keyList.length; j += processNumber) { for (let i = 0; i < processNumber; i++) { if (i + j < keyList.length) { promise[(j + i) % processNumber] = promise[(j + i) % processNumber].then(() => func(keyList[i + j])).catch(({ fileInfo, err }) => { if (fileInfo.status === 3) { console.log(fileInfo.file.name, '取消上传') } else { fileInfo.status = 0 fileInfo.failTryCount -= 1 if (fileInfo.failTryCount < 1) { ElMessage.error(`${fileInfo.file.name} 超过最大重试次数,停止上传`) } else { ElMessage.error(`${fileInfo.file.name} 上传失败,正在重试`) console.log(fileInfo.file.name, err) multiUpload() } } }) } } } }
uploadAsync(fileInfo)
上传文件逻辑,进行文件的上传,进度计算等功能。本功能是模拟上传阿里云盘操作
function uploadAsync(fileInfo) { const progress = fileInfo.progress const file = fileInfo.file return new Promise((resolve, reject) => { progress.file_name = file.name progress.file_size = file.size if (fileInfo.status === 0) { fileInfo.status = 1 } else { return resolve() } progress.progress = 10 getUploadSid().then(async res => { // ElMessage.info(fileName + ' 文件读取中') progress.speed = '文件读取中' let hash = await PreHash(file, progress) let fileHashInfo = { sid: res.data.sid, file_name: progress.file_name, file_size: progress.file_size, pre_hash: hash } progress.progress = 20 checkPreHash(fileHashInfo).then(async pRes => { if (pRes.data.check_status === true) { // 秒传逻辑 progress.progress = 30 const md5Code = pRes.data.md5_token progress.speed = '文件校验中' // ElMessage.info(fileInfo.file_name + ' 秒传检测中') let hash = await ContentHash(file, md5Code, progress) fileHashInfo.proof_code = hash.proofCode fileHashInfo.content_hash = hash.conHash checkContentHash(fileHashInfo).then(async cRes => { if (cRes.data.check_status === true) { progress.progress = 100 progress.upload_size = progress.file_size progress.speed = '秒传成功' // ElMessage.success(fileName + ' 上传成功') fileInfo.status = 2 fileInfo.upload_time = new Date() multiUpload() resolve() } else { return await ChunkedUpload(fileInfo, fileHashInfo, cRes.data.upload_extra, cRes.data.part_info_list, () => { fileInfo.status = 2 fileInfo.upload_time = new Date() multiUpload() resolve() }, (err) => { reject({fileInfo, err}) }) } }).catch((err) => { reject({fileInfo, err}) }) } else { return await ChunkedUpload(fileInfo, fileHashInfo, pRes.data.upload_extra, pRes.data.part_info_list, () => { fileInfo.status = 2 fileInfo.upload_time = new Date() multiUpload() resolve() }, (err) => { reject({fileInfo, err}) }) } }).catch((err) => { reject({fileInfo, err}) }) }).catch((err) => { reject({fileInfo, err}) }) }) }
核心上传大概就是这些逻辑
代码源码:xshare/upload.js at main · nineaiyu/xshare · GitHub
总结
加载全部内容