Vue3 + Element Plus 实现 ZIP 压缩包在线预览(支持图片插入 / 文件预览)

在前端业务开发中,ZIP 压缩包上传后在线预览 是非常常见的需求(比如附件管理、文档预览、图片包查看)。本文基于Vue3 + TypeScript + Element Plus,实现了一套完整的 ZIP 压缩包预览功能:

✅ 解析 ZIP 内部文件列表

✅ 支持 ZIP 内图片 / 文档在线预览

✅ 支持 ZIP 内图片一键插入到编辑器

✅ 弹窗拖拽 + 缩放,交互更友好

✅ 全程无刷新,纯前端解析,体验流畅

一、整体实现思路

  1. 父页面点击预览 ,判断文件是否为.zip格式;
  2. 若是 ZIP,打开专属预览弹窗组件,传递文件 ID;
  3. 组件加载zip.js库,请求 ZIP 文件流并前端解析
  4. 渲染 ZIP 内部文件列表,支持二次预览、图片插入;
  5. 关闭弹窗自动清空数据,避免内存泄漏。

二、父页面代码(调用层)

父页面负责判断文件类型控制弹窗显示传递事件

1. 预览按钮(表格行操作)

javascript 复制代码
<el-button link type="primary" @click="showAttachment(scope.row)"> 预览 </el-button>

2. 点击预览逻辑(判断是否为 ZIP)

javascript 复制代码
// 预览附件
const showAttachment = (fileInfo) => {
  // 正则判断文件名是否为 .zip 格式(不区分大小写)
  const isZip = /\.(zip)$/i.test(fileInfo.fileName)
  // 如果是ZIP,打开ZIP预览弹窗
  if (isZip) {
    zipFileId.value = fileInfo.fileId
    zipDiaglogVisible.value = true
    return
  }
  // 非ZIP,执行普通文件预览
  emit('showAttachment', fileInfo)
}

// 关闭ZIP弹窗
const closeZipDiaglog = () => {
  zipDiaglogVisible.value = false
}

// 预览ZIP内部文件(透传给普通预览)
const showZipEntry = (fileInfo) => {
  emit('showAttachment', fileInfo)
}

3. 引入 ZIP 预览组件

javascript 复制代码
<!-- ZIP预览弹窗组件 -->
<PopupZip
  :zipFileId="zipFileId"
  :diaglogVisible="zipDiaglogVisible"
  @closeDiaglogVisible="closeZipDiaglog"
  @showAttachment="showZipEntry"
/>

三、核心组件:PopupZip 压缩包预览组件

这是完整可运行的组件代码,我会拆分模块 + 逐行解读,方便你直接使用。

1. 模板部分(Dialog + 文件列表)

javascript 复制代码
<template>
  <!-- 可拖拽缩放的对话框 -->
  <el-dialog
    width="600px"
    class="ba-operate-dialog attachment-list-dialog"
    :close-on-click-modal="false"
    :model-value="diaglogVisible"
    @close="closeDiaglog"
    :destroy-on-close="true"
  >
    <template #header>
      <!-- 自定义拖拽标题 -->
      <div class="title" v-drag="['.ba-operate-dialog', '.el-dialog__header']" v-zoom="'.ba-operate-dialog'">
        压缩文件列表
      </div>
    </template>

    <!-- 文件列表 -->
    <div class="attachment-wrapper" style="width: 100%">
      <el-table :data="fileList" border style="width: 100%; height: 320px">
        <!-- 文件名 -->
        <el-table-column prop="fileName" label="附件" show-overflow-tooltip />
        
        <!-- 操作列:预览 + 插入 -->
        <el-table-column fixed="right" label="操作" width="110">
          <template #default="scope">
            <!-- 预览按钮:仅支持可预览文件 -->
            <el-button
              :disabled="!canPreviewFile(scope.row.fileName)"
              link
              type="primary"
              @click="showAttachment(scope.row)"
            >
              预览
            </el-button>
            
            <!-- 插入按钮:仅支持图片 -->
            <el-button
              :disabled="!canCopyFile(scope.row.fileName)"
              link
              type="primary"
              @click="copyAttachment(scope.row)"
            >
              插入
            </el-button>
          </template>
        </el-table-column>
      </el-table>
    </div>
  </el-dialog>
</template>

2. 脚本部分(核心逻辑 + TS 类型)

javascript 复制代码
<script setup lang="ts">
import { ref, onMounted, watch } from 'vue'
import { saveAs } from 'file-saver'
import { downloadMinioFile } from '/@/api/backend/project'
import { canPreviewFile, canCopyFile, dynamicLoadJs } from '/@/utils/common'
import { ElMessage } from 'element-plus'

// 1. TS 类型定义(规范入参)
interface Props {
  diaglogVisible: boolean  // 控制弹窗显示
  zipFileId: string        // ZIP文件ID
}

// 2. 定义Props + 默认值
const props = withDefaults(defineProps<Props>(), {
  diaglogVisible: false,
})

// 3. 自定义事件(向父组件同步状态)
const emit = defineEmits(['closeDiaglogVisible', 'showAttachment'])

// 存储ZIP解析后的文件列表
const fileList = ref<any[]>([])

核心方法 1:关闭弹窗

javascript 复制代码
// 关闭弹窗,通知父组件隐藏
const closeDiaglog = () => {
  emit('closeDiaglogVisible', false)
}

核心方法 2:预览 ZIP 内部文件

javascript 复制代码
// 预览ZIP内的文件(图片/PDF/文档)
const showAttachment = async (fileInfo) => {
  const { entry, fileName } = fileInfo

  // 判断文件是否支持预览
  const isPreview = canPreviewFile(fileName)
  const isPdf = /\.(pdf)$/i.test(fileName)
  if (!isPreview) return

  // 区分PDF与普通文件,创建对应Blob写入器
  const tmp = isPdf ? new zip.BlobWriter('application/pdf') : new zip.BlobWriter()
  
  // 从ZIP条目中提取文件流 → 转成URL → 触发预览
  const blob = await entry.getData(tmp)
  const url = URL.createObjectURL(blob)
  
  // 向父组件抛出自定义预览事件
  emit('showAttachment', {
    fileId: '',
    fileUrl: url,
    fileType: isPdf ? 'pdf' : '',
    fileName,
  })
}

核心方法 3:ZIP 内图片一键插入编辑器

javascript 复制代码
// 向父窗口发送消息,插入图片(适用于富文本编辑器)
const insertImageIntoDoc = async (imageData: any) => {
  const payload = JSON.stringify({
    action: 'insertImage',
    imageData: imageData,
  })
  // 跨窗口通信(iframe/父页面)
  window.parent.postMessage(payload, '*')
}

// 复制/插入图片
const copyAttachment = async (fileInfo: any) => {
  const { entry, fileName } = fileInfo

  // 仅支持图片格式
  const isImage = /\.(jpg|jpeg|png|gif|webp)$/i.test(fileName)
  if (!isImage) {
    ElMessage.warning('目前仅支持复制图片格式文件')
    return
  }

  try {
    // 从ZIP中提取图片Blob
    const blob = await entry.getData(new zip.BlobWriter())
    // 转Base64并插入编辑器
    const reader = new FileReader()
    reader.onload = () => insertImageIntoDoc(reader.result)
    reader.readAsDataURL(blob)
  } catch (err) {
    console.error('复制失败:', err)
  }
}

核心方法 4:动态加载 zip.js + 解析 ZIP 文件

javascript 复制代码
// 解压并解析ZIP文件
const unZipFile = async () => {
  if (!props.zipFileId) return

  // 动态加载zip.js(避免首屏加载过大)
  await dynamicLoadJs('./libs/zip.js/zip.min.js')

  try {
    // 1. 请求后端获取ZIP文件流
    const res = await downloadMinioFile(props.zipFileId)
    // 2. 创建ZIP读取器
    const reader = new zip.ZipReader(new zip.BlobReader(res.data))
    // 3. 获取所有文件条目
    const entries = await reader.getEntries()
    // 4. 格式化数据,渲染表格
    fileList.value = entries.map((entry) => ({
      fileName: entry.filename,
      entry: entry, // 保存条目对象,用于后续提取文件
    }))
  } catch (err) {
    console.error('ZIP解析失败', err)
  }
}

监听弹窗状态(自动加载 / 清空)

javascript 复制代码
// 监听弹窗显示:打开则解析ZIP,关闭则清空数据
watch(
  () => props.diaglogVisible,
  (val) => {
    if (!val) {
      fileList.value = [] // 关闭清空,防止数据残留
      return
    }
    unZipFile() // 打开弹窗自动解析ZIP
  }
)
</script>

四、关键代码深度解读(必看!)

1. ZIP 文件前端解析原理

  • 使用 zip.js 纯前端解析 ZIP,无需后端参与;
  • 通过 BlobReader 读取文件流,ZipReader 解析条目;
  • 每个文件对应一个entry对象,保存了文件名、大小、数据流。

2. 文件预览实现

  • 调用 entry.getData() 从 ZIP 中提取文件;
  • 转成Blob → 生成临时 URL URL.createObjectURL()
  • 抛给父组件执行预览(支持图片 / PDF / 文档)。

3. 图片插入编辑器

  • 读取图片Blob → 转base64
  • 通过 postMessage 跨窗口通信,把图片插入富文本 / 编辑器;
  • 兼容主流编辑器(UEditor、TinyMCE、自定义编辑器)。

4. 性能与安全

  • 动态加载zip.js,不占用主包体积;
  • 关闭弹窗自动清空fileList,防止内存泄漏;
  • 仅解析文件列表,不占用过多内存。

五、使用说明 & 依赖准备

  1. 下载 zip.js 放到 public/libs/zip.js/ 目录;
  2. 实现工具函数:
    • canPreviewFile:判断文件是否支持预览
    • canCopyFile:判断是否为图片
    • dynamicLoadJs:动态加载 JS
  3. 替换文件下载接口 downloadMinioFile 为你项目的下载 API;
  4. 直接引入组件即可使用。

六、效果总结

这套方案在后台管理系统、文档管理、附件预览场景中非常实用,具备以下优势:

✅ 纯前端解析,速度快

✅ 支持二次预览、图片插入

✅ 交互友好(拖拽 + 缩放)

✅ 代码解耦,可直接复用

✅ TS 加持,类型安全,维护简单

如果你需要预览更多格式(Word/Excel),也可以基于这套代码扩展!


总结

  1. 本文实现了Vue3 + TS + Element Plus的 ZIP 在线预览完整方案;
  2. 核心是使用zip.js前端解析压缩包,提取文件流实现预览 / 插入;
  3. 代码结构清晰、注释完善、可直接落地业务;
  4. 支持预览、图片插入、弹窗拖拽,交互体验拉满。
相关推荐
一只小阿乐2 小时前
js流式模式输出 函数模式使用
开发语言·javascript·ai·vue·agent·流式数据·node 服务
伯远医学2 小时前
如何判断提取的RNA是否可用?
java·开发语言·前端·javascript·人工智能·eclipse·创业创新
全栈技术负责人2 小时前
Claw Code 系统架构与 Agent 运行机制解析
前端·系统架构·ai编程
人人常欢笑2 小时前
Grafana 表格自定义下载样式。
javascript·react.js·grafana
x-cmd2 小时前
[x-cmd] 专为 AI Agent 设计的无头浏览器,比 Chrome 速度快 9 倍且少用 16 倍内存 | Lightpanda
前端·chrome·ai·自动化·agent·浏览器·x-cmd
lifewange2 小时前
JavaScript是什么
开发语言·javascript·ecmascript
chxii3 小时前
Nginx 正则 location 指令匹配客户端请求的 URI
前端·nginx
qing222222223 小时前
Linux:/var/log/journal 路径下文件不断增加导致根目录磁盘爆满
linux·运维·前端
Armouy3 小时前
Nuxt.js 学习复盘:核心概念与实战要点
前端·javascript·学习