定制软件主要实现功能
1.定制软件多文件拖拽上传
2.定制软件显示文件上传进度条
3.上传期间,定制软件还可以继续拖拽上传
4.定制软件可以支持手动修改上传并发任务数
5.上传失败自动重试,最大重试次数3次
6.上传过程中可以手动取消上传
本次使用的是element ui ,上传拖拽是通过实现的。
- <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的存储库
下面内容通过定义了一个存储,存储有三个字段
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})
- })
- })
- }
核心上传大概就是这些逻辑
代码源码: