toolgarden.xyz
EN
ZIP文件压缩文件解压浏览器本地处理前端工程

怎么实现文件在线压缩和解压:基于浏览器本地 ZIP 的完整思路

在线压缩和解压不一定要上传服务器。用 File API、fflate、Blob URL 和目录树建模,就可以在浏览器里完成 ZIP 打包、解包、预览目录和单文件下载。

ToolGarden 推荐的工具优先在浏览器本地运行,文件和文本不必上传到服务器,适合更注重安全隐私的日常处理。

发布于 2026年9月8日约 9 分钟阅读作者 ToolGarden

在线压缩和在线解压听起来像服务端任务:上传文件、服务器打包、再下载结果。但如果目标格式是 ZIP,现代浏览器已经足够完成大部分日常场景。文件可以留在用户设备上,页面只负责读取本地文件、运行压缩算法、生成下载链接。

ToolGarden 的 ZIP 压缩和 ZIP 解压工具就是这个思路:压缩页把多个 File 对象转成 ZIP Blob;解压页读取 ZIP 字节,解析出条目,按路径还原目录树,并给每个文件生成单独下载。整个过程没有 API route 接收文件内容。

整体数据流

压缩:
File input / drag drop
  -> File API 读取 Blob
  -> 规范化 ZIP 内路径
  -> fflate zipSync
  -> Blob(application/zip)
  -> URL.createObjectURL 下载

解压:
ZIP File
  -> arrayBuffer
  -> 读取中央目录修正文件名编码
  -> fflate unzipSync
  -> 过滤不安全路径
  -> 构建目录树
  -> 每个文件生成 Blob 下载

这个数据流里最关键的边界是:页面可以读取用户通过文件选择器授权的本地文件,但不会把这些字节发送给服务器。服务端只提供 JavaScript、CSS 和静态资源,真正的处理发生在浏览器进程中。

为什么选择 ZIP,而不是 RAR 或 7z?

ZIP 的创建和解压都有成熟的 JavaScript 实现,系统兼容性也最好。用户下载后的 .zip 可以被 Windows、macOS、Linux 和常见手机系统直接打开。对在线工具来说,这是最稳的默认格式。

RAR 和 7z 的情况不同。它们的解压生态可以评估,但创建端并不像 ZIP 那样开放、轻量、浏览器友好。为了避免功能看似支持、实际兼容性脆弱,压缩工具只生成 ZIP,解压工具也先聚焦 ZIP。

压缩:从多个 File 生成 ZIP Blob

压缩端页面只做 UI 状态:用户选择文件、设置输出文件名、选择压缩等级、点击按钮。真正的处理放在 lib/utils/zip.ts 中,函数接收一组 filename + blob,返回判别联合类型,而不是直接操作 React state。

type ZipCompressionOutcome =
  | {
      ok: true;
      blob: Blob;
      filename: string;
      fileCount: number;
      originalSize: number;
      outputSize: number;
      durationMs: number;
    }
  | { ok: false; code: 'empty_selection' | 'empty_file' | 'zip_failed' };

压缩前需要处理 ZIP 内路径。拖入文件夹时,浏览器可能提供 webkitRelativePath;普通多文件选择只有 file.name。路径要统一替换反斜杠、过滤空片段、过滤 . 和 ..,重复文件名要加后缀,避免后一个文件覆盖前一个文件。

压缩算法使用 fflate 的 zipSync。压缩等级 0 到 9,0 更像“只打包不压缩”,9 通常体积更小但耗时更长。默认 6 是比较合理的速度和体积平衡。

解压:先得到文件条目,再构建目录树

解压端不是把所有文件平铺成列表,而是把 ZIP 内部路径拆成目录树。例如 docs/readme.txt 和 docs/assets/logo.png 会共享 docs 目录,assets 是 docs 的子目录。UI 里目录行可以展开折叠,文件行显示大小和下载按钮。

interface ZipExtractedEntry {
  path: string;
  name: string;
  size: number;
  blob: Blob;
}

interface TreeNode {
  name: string;
  path: string;
  children: Map<string, TreeNode>;
  entry?: ZipExtractedEntry;
}

目录树最好在页面层用纯数据结构生成,避免把展示状态和解压逻辑搅在一起。解压函数只返回 entries;React 组件再根据 entries 构建 TreeNode,并维护 expanded 这类交互状态。

中文文件名为什么会乱码?

很多 ZIP 文件名是 UTF-8,但并不是所有 ZIP 都会设置 UTF-8 标志。国内常见压缩软件生成的旧 ZIP,文件名可能实际是 GBK 或 GB18030 字节。如果解压库按 Latin-1 或错误编码解释这些字节,中文名就会变成乱码。

解决办法是读取 ZIP 中央目录里的原始文件名字节:如果条目没有 UTF-8 filename flag,就把这些字节用 GB18030 解码,再映射回解压库返回的 Latin-1 名称。这样既不破坏标准 UTF-8 ZIP,也能兼容中文 Windows 环境里常见的旧压缩包。

下载:用 Blob URL,而不是服务器文件

无论压缩还是解压,下载都可以用 Blob URL。压缩结果是 application/zip Blob;解压后的每个文件是 application/octet-stream Blob。页面调用 URL.createObjectURL,放到 a 标签的 href 上,再设置 download 文件名即可。

Blob URL 是临时资源,页面关闭后会消失。长期运行的工具页还要在清空结果或组件卸载时调用 URL.revokeObjectURL,避免用户连续处理多个大文件时堆积内存。

必须处理的安全和稳定性边界

  • Zip Slip:ZIP 条目路径里可能包含 ../,展示和下载前必须过滤上级目录片段。
  • zip bomb:压缩包体积很小但解压后极大,浏览器端应提示大文件风险,并在后续版本加入数量和总大小上限。
  • 加密 ZIP:密码交互和兼容性复杂,当前工具应明确提示不支持,而不是吞掉错误。
  • 内存压力:几百 MB 的 ZIP 或大量文件会占用浏览器内存,应优先保持 UI 可恢复、错误可理解。
  • 文件名冲突:压缩时同名文件必须自动重命名,解压下载时至少要保留基础文件名。

工程分层怎么放?

在 ToolGarden 里,ZIP 能力遵循 Harness Engineering:工具元数据进 registry,文案进 messages,ZIP 算法进 lib/utils/zip.ts,页面组件只处理 useState、文件选择、目录展开和下载点击。这样首页、Other tools 菜单、面包屑、SEO、sitemap 和 llms 文件都能从同一套元数据自动派生。

常见问题

Q.在线压缩和解压一定需要服务器吗?

不一定。ZIP 这类格式可以在浏览器里用 File API 读取、用 JavaScript 库压缩或解压,再用 Blob URL 下载。服务器只负责提供页面资源,不必接收用户文件。

Q.为什么 ZIP 解压后的中文文件名会乱码?

因为一些 ZIP 没有设置 UTF-8 文件名标志,但文件名字节实际是 GBK 或 GB18030。需要读取中央目录里的原始文件名字节,并在没有 UTF-8 标志时使用 GB18030 fallback 解码。

Q.浏览器本地 ZIP 工具适合处理多大的文件?

取决于设备内存和浏览器。普通附件和资料包通常没问题;几百 MB 以上、大量文件或可疑压缩包可能变慢或失败,应提示用户风险并考虑分批处理。