Element UI的el-upload文件上传与ZIP文件解压上传

在实现文件上传功能时,有需要支持多种文件格式并处理的特殊需求。本文将分享如何使用Element UI的el-upload组件实现支持普通文件和ZIP文件解压的混合上传方案。

技术方案核心

  • 上传组件:Element UI的el-upload组件作为基础
  • 文件解压:JSZip处理ZIP文件内容
  • 编码处理:iconv-lite解决中文文件名乱码问题
  • 混合上传:
    1. 普通文件:使用el-upload原生提交
    2. 解压文件:通过axios单独上传
核心实现代码
js 复制代码
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文件。

js 复制代码
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上传解压后的文件

js 复制代码
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} 失败`)
        }
      }
    },

文件上传与上传成功后的处理

js 复制代码
  /**
     * 开始上传文件
     */
    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 = []
    },

有更好建议或发现错误,欢迎指正!

相关推荐
陈拉斯1 小时前
上线一个多人扫码网页娱乐工具,我在并发、SEO、PWA 和打包脚本上踩的 8 个坑
javascript
用户298698530142 小时前
在 React 中实现 RTF 与 PDF、HTML 的文档转换实践
javascript·react.js·html
catastrophe_zy2 小时前
如何用 WebCodecs 在浏览器里实现高清录屏 —— 无插件、无水印、直接导出 MP4
前端·javascript·录屏
Moment3 小时前
太好了!NestJS 12 大版本转向 ESM,新项目默认构建换 Rspack
前端·javascript·后端
平头哥~4 小时前
Day 15 | 不改一行 HTML,给页面加上引号、角标和标签
前端·javascript·css·html·css3·学习资料
Fluxart.ai5 小时前
GPT Image 2 国内怎么用?在 Flux Art 完成图片生成与编辑
前端·javascript·gpt
circuitsosk13 小时前
任务规划器的三种范式对比:ReAct、Plan-and-Execute 与 Tree-of-Thought 在真实业务中的取舍
前端·javascript·python·react.js·llm·ai agent
小磊哥er14 小时前
深入解构Claude Code - 第 12 篇 · 整体串起来
javascript·ai编程
小磊哥er14 小时前
深入解构Claude Code - 第 11 篇 · 工程上的讲究
javascript·ai编程