在实现文件上传功能时,有需要支持多种文件格式并处理的特殊需求。本文将分享如何使用Element UI的el-upload组件实现支持普通文件和ZIP文件解压的混合上传方案。
技术方案核心
- 上传组件:Element UI的el-upload组件作为基础
- 文件解压:JSZip处理ZIP文件内容
- 编码处理:iconv-lite解决中文文件名乱码问题
- 混合上传:
◦ 普通文件:使用el-upload原生提交
◦ 解压文件:通过axios单独上传
核心实现代码
import JSZip from 'jszip'
import axios from 'axios'
data() {
return {
uploadUrl: '',
accept: '.zip,.doc,.docx,.txt,.pdf,.xls,.xlsx,.jpeg,.jpg,.png',
uploadHeaders: { Authorization: 'Bearer ' + getToken() },
uploadedFiles: [],
fileList: [],
}
},
在on-change事件中解压zip文件。解压成功,清除zip文件,将解压的文件放入el-upload的fileList中;解压失败,过滤掉zip文件。
async handleFileChange(file, fileList) {
this.fileList = fileList // 更新文件列表
this.hasFiles = fileList.length > 0 // 更新文件标志
if (file.name.toLowerCase().endsWith('.zip') && this.zipFg) {
try {
const zip = new JSZip()
const content = await file.raw.arrayBuffer()
let iconv = require('iconv-lite')
const zipContent = await zip.loadAsync(content, {
decodeFileName: function (bytes) {
return iconv.decode(bytes, 'gbk')
}
})
const pdfFiles = []
await Promise.all(
Object.values(zipContent.files).map(async (entry) => {
if (!entry.dir) {
const blob = await entry.async('blob') // 使用 await 等待 blob 获取完成
pdfFiles.push({
name: entry.name,
blob,
status: 'success',
percentage: 0,
extracted: true,
})
}
})
)
const nonZipFiles = fileList.filter((f) => !f.name.toLowerCase().endsWith('.zip'))
this.fileList = [...nonZipFiles, ...pdfFiles]
} catch (err) {
const nonZipFiles = fileList.filter((f) => !f.name.toLowerCase().endsWith('.zip'))
this.fileList = [...nonZipFiles]
this.$message.error('解压失败')
}
}
},
使用axios上传解压后的文件
async uploadPdfFiles(pdfFiles) {
for (const pdfFile of pdfFiles) {
const formData = new FormData()
formData.append('file', pdfFile.blob, pdfFile.name)
try {
const response = await axios.post(this.uploadUrl, formData, {
headers: {
...this.uploadHeaders, // 使用上传请求头
'Content-Type': 'multipart/form-data',
},
})
this.uploadedFiles.push({
fileName: response.data.data.name,
url: response.data.data.url,
status: 'success',
})
} catch (error) {
this.$message.error(`上传文件 ${pdfFile.name} 失败`)
}
}
},
文件上传与上传成功后的处理
/**
* 开始上传文件
*/
async startUpload() {
// 校验是否有文件
if (!this.hasFiles) {
this.$message.warning('请先选择文件后再上传!')
return
}
// 这里可设置上传状态
// 分离解压文件和普通文件
const normalFiles = this.fileList.filter((f) => !f.extracted)
const extractedFiles = this.fileList.filter((f) => f.extracted)
// 1. 上传普通文件
if (normalFiles.length > 0) {
this.$refs.upload.submit()
return
}
// 2. 上传解压文件
if (extractedFiles.length > 0) {
await this.uploadPdfFiles(extractedFiles)
if(this.uploadedFiles.length === 0) {
return
}
this.$message.success(`文件上传成功.`)
setTimeout(() => {
this.uploadSuccess()
}, 500)
}
},
async handleUploadSuccess(res, file, fileList) {
if (res.code === 200) {
this.uploadedFiles.push({
fileName: file.name,
url: res.data.url,
status: file.status,
})
let files = this.fileList.filter((f) => !f.blob)
if (files.every((f) => f.status === 'success')) {
const extractedFiles = this.fileList.filter((f) => f.extracted)
if (extractedFiles.length > 0) {
await this.uploadPdfFiles(extractedFiles)
if(this.uploadedFiles.length === 0) {
return
}
}
this.$message.success(`文件上传成功.`)
setTimeout(() => {
this.uploadSuccess()
}, 500)
}
} else {
this.$message.error(`文件 ${file.name} 上传失败.`)
}
},
// 上传成功后的处理,包含重置以及通知父组件等
uploadSuccess() {
this.uploadedFiles.forEach(item => {
delete item.status
});
this.$emit('uploadedFiles', this.uploadedFiles)
this.uploadedFiles = []
this.fileList = []
},
有更好建议或发现错误,欢迎指正!