「这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战」 |
---|
最近有个任务,写个上传页面,是个后台页面,所以首选用Element-UI中的<el-upload>
。
因为需要分片上传,所以自定义了上传事件http-request
,但是跟后台联调的时候发现怎么file-list列表中的进度条怎么没有了...., 刚好UI给的效果图跟upload给出的有点区别,那就自定义一下好了。
1. 页面部分代码
- 隐藏默认的file-list列表
- 声明自定义上传事件
http-request
<el-upload
class="upload-demo"
ref="upload"
action="/"
multiple //多文件上传
:action="uploadUrl" //文件上传的地址(必填)
:show-file-list="false" //自定义样式所以设置false不显示
:file-list="fileArr"//文件列表
:http-request="checkedFile" //自定义上传事件
:before-upload="beforeUploadFile" //文件上传前
:on-progress="uploadFileProcess" //文件传输过程(处理进度条)
:on-success="handleFileSuccess" //文件成功>
<el-button size="small" type="primary">上次文件</el-button>
</el-upload>
2.js部分代码
// 格式化文件大小显示文字
getSize(size) {
return size > 1024
? size / 1024 > 1024
? size / (1024 * 1024) > 1024
? (size / (1024 * 1024 * 1024)).toFixed(2) + 'GB'
: (size / (1024 * 1024)).toFixed(2) + 'MB'
: (size / 1024).toFixed(2) + 'KB'
: size.toFixed(2) + 'B'
},
async checkedFile(options) {
const { maxSize, multiUploadSize, getSize, splitUpload, singleUpload } =
this
const { file, onProgress, onSuccess, onError } = options
console.log('options', options, this)
if (file.size > maxSize) {
return this.$message({
message: `您选择的文件大于${getSize(maxSize)}`,
type: 'error',
})
}
// 大于指定大小切片上传
const uploadFunc = file.size > multiUploadSize
? splitUpload
: singleUpload
try {
let info = await uploadFunc(file, onProgress)
this.$message({
message: info.msg || '上传成功',
type: 'success',
})
onSuccess()
} catch (e) {
console.error('error', e)
onError()
}
const prom = new Promise((resolve, reject) => {})
prom.abort = () => {}
return prom
},
// 大文件分块上传
splitUpload(file, onProgress) {
return new Promise(async (resolve, reject) => {
try {
const { eachSize } = this
const chunks = Math.ceil(file.size / eachSize)
const fileChunks = await this.splitFile(file, eachSize, chunks)
let currentChunk = 1
let cbRes = []
for (let i = 1; i <= fileChunks.length; i++) {
// 服务端检测已经上传到第currentChunk块了,那就直接跳到第currentChunk块,实现断点续传
console.log('before', currentChunk, i)
if (currentChunk === i) {
// 每块上传完后则返回需要提交的下一块的index
let postRes = await this.postFile(
{
// chunked: true,
chunk: i,
chunks,
// eachSize,
name: file.name,
totalSize: file.size,
// uid: file.uid,
model: this.model,
file: fileChunks[i - 1],
},
onProgress
)
// 根据实际接口格式判断是否续传
let status = postRes.key
if (status === 'continue') {
currentChunk = postRes.value
} else if (status === 'hasValue') {
currentChunk = postRes.value + 1
} else if (status === 'success') {
currentChunk = postRes.value
cbRes = postRes
} else {
currentChunk = postRes.value
}
console.log('after currentChunk', currentChunk)
}
}
resolve(cbRes)
} catch (e) {
reject(e)
}
})
},
// 文件分块,利用Array.prototype.slice方法
splitFile(file, eachSize, chunks) {
return new Promise((resolve, reject) => {
try {
setTimeout(() => {
const fileChunk = []
for (let chunk = 0; chunks > 0; chunks--) {
fileChunk.push(file.slice(chunk, chunk + eachSize))
chunk += eachSize
}
resolve(fileChunk)
}, 0)
} catch (e) {
console.error(e)
reject(new Error('文件切块发生错误'))
}
})
},
// 提交文件方法,将参数转换为FormData, 然后通过axios发起请求
postFile(param, onProgress) {
const formData = new FormData()
for (let p in param) {
formData.append(p, param[p])
}
const { requestCancelQueue } = this
const config = {
cancelToken: new axios.CancelToken(function executor(cancel) {
if (requestCancelQueue[param.uid]) {
requestCancelQueue[param.uid]()
delete requestCancelQueue[param.uid]
}
requestCancelQueue[param.uid] = cancel
}),
onUploadProgress: (e) => {
console.log('onUploadProgress', e, param, this)
if (param.chunked) {
e.percent = Number(
(
((param.chunk * (param.eachSize - 1) + e.loaded) /
param.fullSize) *
100
).toFixed(2)
)
} else {
e.percent = Number(((e.loaded / e.total) * 100).toFixed(2))
}
onProgress(e)
},
}
// 实际上传事件
if (this.fileType === 0) {
return uploadStructTree(formData, config).then((rs) => rs)
} else if (this.fileType === 2) {
return uploadTheory(formData, config).then((rs) => rs)
} else if (this.fileType === 3) {
return uploadDmo(formData, config).then((rs) => rs)
} else {
return upload3DToBos(formData, config).then((rs) => rs)
}
},