2026-10-16 17:57:58 +08:00

ZipVeil — 一个文件,两副面孔。

给文件,换一件外衣。

将压缩包封装在视频或图片尾部,用命令行或桌面界面创建、提取与校验。

Go 1.26+ Wails v3 Beta 默认 AES-256 加密 CLI 与桌面版

安装与编译 · 桌面版指南 · 命令行用法 · 文件原理


ZipVeil 能做什么

ZipVeil 是一个用 Go 编写的本地归档工具:将文件压缩后封装到 MP4、JPG / JPEG、PNG、GIF 或 WebP,并支持创建普通 ZIP、TAR 和常见压缩流,或从 ZIP、7Z、RAR、TAR 等归档及封装文件中提取内容。提供独立的 CLI 与桌面版本,所有文件处理均在本机完成。

  • 视频或图片封面:选择 MP4、JPG / JPEG、PNG、GIF 或 WebP 与文件或目录,默认使用 AES-256 加密,保留原始封面字节。
  • 常用归档格式:创建 ZIP、TAR、GZIP、BZIP2、XZ、Zstandard 和 TAR 组合格式;读取 ZIP、7Z、RAR / RAR5 以及这些格式。
  • 直接提取文件:读取视频或图片中的归档和普通压缩包,无需修改扩展名;CLI 与桌面版均支持批量智能解压。
  • 查看与校验归档:浏览文件列表、识别加密信息,完整读取文件检查 CRC 与 AES 认证。
  • 选择你的操作方式:桌面版支持文件拖放与进度显示,CLI 适合脚本和自动化。

封面负责伪装,加密负责保护内容;ZIP 文件名和部分元数据仍然可见。显示或播放兼容性取决于读取软件,图片重新压缩可能丢失附加归档,详见兼容性说明。

安装与编译

当前首个测试版为 v0.1.0-beta.2。前往 Gitea Releases 下载 CLI 或桌面版,校验附件 SHA256SUMS。自动发布提供 Linux / macOS / Windows 的 AMD64 与 ARM64 CLI,以及 Windows AMD64、构建 runner 原生架构的 Linux 桌面版;macOS 桌面版暂需本机编译。完整范围见版本说明。运行 zipveil-cli --version 可查看版本。

需要 Go 1.26 或更新版本。首次构建时 Go 会下载 go.mod 中的依赖:

go build -o zipveil .

Windows:

$env:GOOS = "windows"
$env:GOARCH = "amd64"
go build -o zipveil.exe .

其他平台可设置 GOOS / GOARCH 后执行相同的 go build。归档处理使用纯 Go 库,不依赖外部 7-Zip 程序或 CGo;桌面窗口本身仍使用 Wails 的平台依赖。

桌面界面与两种构建产物

项目提供两个独立入口,共用 internal/zipveil 中的归档处理逻辑:

版本 入口 产物
CLI 仓库根目录 zipveil-cli,Windows 为 zipveil-cli.exe
桌面 UI desktop/ zipveil-ui / zipveil-ui.exe,macOS 可打包为 zipveil-ui.app

桌面版采用固定版本 Wails v3.0.0-beta.30 + Vue + TypeScript,支持多个文件与文件夹打包、智能提取、归档内容浏览与搜索、完整性校验和文件拖放。单个归档可勾选文件或目录按需提取;进度显示处理阶段、本阶段已处理大小、速度与总耗时,速度稳定后才显示本阶段预计剩余时间。任务执行中可取消;批量提取支持归档队列、共用或单独密码,以及失败项重试。取消批量任务时保留已完成结果,清理当前归档的未完成临时内容。

桌面版侧栏“设置”可调整总展开大小、单文件大小和条目数上限,支持本地保存与恢复默认值;保存后从下一次提取或校验任务开始生效。

两个入口使用独立的 Go module,桌面依赖不会进入 CLI 的依赖树;原有 CLI 构建方式继续适用。命令行版的明确命名示例:

go build -trimpath -o dist/zipveil-cli .
GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -trimpath -o dist/zipveil-cli.exe .

桌面版安装、开发和平台构建说明见 desktop/README.md。已安装 Wails CLI 后,也可以在仓库根目录执行 make cli、make ui,macOS 应用包使用 make ui-macos。

支持的压缩包格式

支持范围参考 Bandizip 与 7-Zip 的常用格式,实际能力以本表为准。所有归档功能使用纯 Go 库,安装包无需携带或调用 7-Zip 程序。

格式 创建 浏览 / 解压 / 校验 封面封装 密码
ZIP ✓ ✓ 新建 / 已有 可选 WinZip AES;读取标准 ZIP 加密
TAR ✓ ✓ 新建 / 已有 不支持
TAR.GZ / TGZ ✓ ✓ 新建 / 已有 不支持
TAR.BZ2 / TBZ / TBZ2 ✓ ✓ 新建 / 已有 不支持
TAR.XZ / TXZ ✓ ✓ 新建 / 已有 不支持
TAR.ZST / TZST ✓ ✓ 新建 / 已有 不支持
GZ / GZIP、BZ2 / BZIP2、XZ、ZST / ZSTD 单文件 ✓ 新建 / 已有 不支持
7Z — ✓ 已有归档 支持内容和目录加密读取
RAR / RAR5 — ✓ 已有归档 支持加密读取

ZIP 兼容的 JAR、APK、EPUB、XPI、DOCX 等容器可按内容读取。ZIP 除 Store / Deflate 外可读取 BZIP2、XZ、Zstandard(方法 20 / 93);不代表支持所有 ZIPX 算法。TAR 组合格式自动进入内层 TAR,单文件压缩流解压为一个文件。固实 7Z / RAR 为定位后面的文件可能需解码前面的数据,勾选提取只写出所选内容。

本轮不支持创建 7Z / RAR、分卷归档,也不包含 ISO、CAB、DMG、WIM、ALZ、EGG 等其他格式。损坏文件或库未实现的编码会明确报错。归档内部的符号链接、硬链接、特殊文件和 TAR 稀疏文件不写出。

新增后端来自 bodgit/sevenzip、nwaples/rardecode、klauspost/compress、dsnet/compress、ulikunitz/xz 和 Go 标准库。分别固定版本于 go.mod;无需安装这些项目的命令行工具。

ZIP/AES 库

项目依赖独立维护的 ByteVibe ZIP 模块 gitea.bytevibe.dev/gary/zip。该库源自 github.com/yeka/zip,并提供 ZipVeil 使用的 WinZip AES、标准 ZIP 加密和 Writer 级 Deflate 压缩器配置。依赖版本固定在 go.mod 的提交伪版本;更新库时应先在独立仓库完成测试和推送,再更新这里的版本。

仓库保留小型演示文件 data/demo.mp4;本地素材 data/zip_source/、解压输出和编译产物不纳入版本控制。

用法

检查文件

./zipveil inspect data/demo.mp4
./zipveil inspect --list data/demo.mp4

命令会显示 ZIP 条目数量、加密状态、WinZip AES 参数、ZIP 起始偏移,以及 MP4 或图片封面格式。加上 --list 会逐条列出文件。列出条目不需要密码。文件名默认使用 auto:有 UTF-8 标记时按 UTF-8 解码,没有标记时若 GBK 可解出中文则按 GBK 显示,否则按 ZIP 约定回退到 CP437。可用 --filename-encoding cp437 或 --filename-encoding gbk 强制选择。

解压

./zipveil extract data/demo.mp4 -o ./demo-extracted

默认通过终端安全提示输入密码。也可以显式提供密码:

./zipveil extract data/demo.mp4 -o ./demo-extracted --password '你的密码' --password-encoding auto

--password-encoding auto 按 UTF-8、GBK、GB18030、Big5 的顺序尝试密码字节,并对候选字节去重。它与文件名编码是两个独立选项。可以指定单一密码编码以缩短验证时间:

./zipveil extract data/demo.mp4 -o ./demo-extracted --password-encoding gbk

如果 ZIP 中未声明 UTF-8 的文件名不是 GBK,可指定文件名编码,例如 --filename-encoding cp437 或 --filename-encoding big5。

解压前会使用最小的非空加密文件验证密码,并在完整读取时检查 AES 认证和适用的 CRC。当前一次解压使用同一个密码处理所有加密条目。输出目录必须尚不存在;工具先写入临时目录,全部条目成功后再发布。绝对路径、路径穿越和符号链接条目会被拒绝。密码也可以通过 --password 参数传入,但命令行参数可能被本机进程查看;交互式提示更安全。

解压会恢复普通文件及归档中显式目录条目的修改时间和普通权限位(rwx),目录元数据在子文件写完后恢复。优先使用 ZIP 的 UTC 扩展时间字段,否则使用传统 DOS 时间(精度为 2 秒);缺失时间时保留新建时间。缺失或不适用于目录的权限使用文件 644、目录 755。自动补建但没有对应 ZIP 条目的目录采用默认权限。不会恢复所有者、ACL、扩展属性、setuid/setgid 等特殊权限。Windows 和不支持 Unix 权限的文件系统按自身能力处理;文件系统也可能对时间精度进行舍入。

为避免异常 ZIP 占满磁盘,解压默认限制展开后的总大小为 20 GiB、单个文件为 10 GiB、条目数为 100,000。可通过 --max-total-size、--max-file-size 和 --max-entries 修改;大小支持字节、十进制 KB/MB/GB/TB 和二进制 KiB/MiB/GiB/TiB,设为 0 表示取消对应限制。例如:

./zipveil extract data/demo.mp4 -o ./demo-extracted --max-total-size 50GiB --max-file-size 0 --max-entries 200000

程序先检查归档声明的大小和条目数,再在实际读取时限制写出量。压缩流需先解码到私有临时文件以识别并索引内层 TAR;这一步受总展开大小限制,计算包含 TAR 的头部和填充,并会在完成、失败或取消后删除。Zstandard 和 RAR 解码字典额外限制为 256 MiB;XZ 按文件声明的字典解码。

完整性测试

./zipveil test data/demo.mp4
./zipveil test data/demo.mp4 --password '你的密码' --max-total-size 50GiB

test 会完整读取所有文件,检查 ZIP CRC 和 WinZip AES 认证标签,不会写出解压文件。加密压缩包会安全提示输入密码;也支持和 extract 相同的密码编码、文件名编码及资源上限选项。此命令可用于解压前检查压缩包是否损坏或密码是否正确。

在交互式终端中,解压时会在标准错误上显示并动态刷新一行进度和当前文件路径,例如 [ 42/100%]正在解压:目录/文件.bin;解压进度按已写出的文件内容计算,验证密码时会显示正在校验的文件并暂留在 0%。重定向输出时不显示动态进度行,以免污染脚本输出。

批量智能解压

./zipveil extract-all ./archives -o ./unpacked

命令按文件名顺序检查指定目录中的普通文件,按内容识别归档后依次解压;可混合处理 ZIP、7Z、RAR、TAR、压缩流、MP4 和图片封装。无法识别的普通文件会忽略。加密归档会逐个提示密码;如果所有归档密码相同,可以传 --password,密码编码和文件名编码也可通过对应选项设置。

智能目录规则是:归档内所有条目都位于同一个顶层文件夹下,且没有根目录散落文件时,直接保留这个顶层文件夹;否则新建一个以归档文件名(不含扩展名)命名的文件夹,再将内容解压进去。例如 photos.zip 若只有 photos/1.jpg,结果是 unpacked/photos/1.jpg;若归档根目录直接有 readme.txt,结果是 unpacked/photos/readme.txt。默认遇到同名目标时使用 --on-conflict rename,自动选择 名称 (2)、名称 (3) 等新目录,不覆盖已有内容。也可以用 --on-conflict skip 保留已有目录并跳过该归档,或用 --on-conflict error 将冲突计为失败。批量解压同样支持上述解压资源上限。此命令只检查输入目录的第一层,不递归扫描子目录。

创建普通压缩包

不传封面时创建普通压缩包,用 --format 指定格式(默认 ZIP)。普通 ZIP 不填密码时不加密;传入 --password 或 --password-stdin 后使用 AES。TAR 和压缩流若指定密码会报错,不会静默忽略密码。

./zipveil pack --format zip -source ./notes.txt -source ./photos -o ./bundle.zip
./zipveil pack --format zip -source ./payload -o ./encrypted.zip --password-stdin < ./password.txt
./zipveil pack --format tar.gz -source ./payload -o ./bundle.tar.gz
./zipveil pack --format tar.xz -source ./payload -o ./bundle.tar.xz
./zipveil pack --format tar.zst -source ./payload -o ./bundle.tar.zst
./zipveil pack --format gz -source ./large.log -o ./large.log.gz
./zipveil extract ./bundle.tar.gz -o ./unpacked
./zipveil test ./archive.7z

gz、bz2、xz、zst 只接受一个普通文件,多个文件或文件夹请用对应的 tar.* 组合格式。--level 1-9 用于 ZIP Deflate、GZIP、BZIP2、Zstandard;XZ 目前使用固定的 8 MiB 字典,TAR 无压缩级别。

加密目录在浏览时需要密码:zipveil inspect --list --password-stdin archive.7z < password.txt。7Z / RAR 使用 Unicode 密码,密码编码请选择 auto 或 utf-8;传统密码编码选项用于 ZIP。无需密码的归档不会消费标准输入。

使用视频或图片封装归档

准备一个 MP4、JPG / JPEG、PNG、GIF 或 WebP 作为封面,以及待压缩文件或目录。传入单个文件时,压缩包内会使用该文件名;传入目录时,会保留目录下的相对路径:

./zipveil pack --cover ./cover.mp4 -source ./payload -o ./cover-with-files.mp4
# 图片封面,输出保留对应图片格式
./zipveil pack --cover ./cover.jpg -source ./payload -o ./cover-with-files.jpg
./zipveil pack --cover ./cover.png -source ./payload -o ./cover-with-files.png
./zipveil pack --cover ./cover.gif -source ./payload -o ./cover-with-files.gif
./zipveil pack --cover ./cover.webp -source ./payload -o ./cover-with-files.webp

默认创建 ZIP,提示输入密码并使用 AES-256 加密。封面封装也支持所有可创建格式,以及原样封装已有归档(含 7Z、RAR / RAR5)。按内容识别封面格式,输出扩展名须与识别格式一致,JPEG 支持 .jpg 和 .jpeg。旧版 -video 仍可使用,与 --cover 互斥。-source 可以是单个普通文件,也可以是目录。也可以指定编码和 AES 强度:

./zipveil pack -video ./cover.mp4 -source ./payload -o ./cover-with-files.mp4 \
  --password-encoding utf-8 --compression deflate --level 5 --aes 256

桌面版“封面封装”可选择“新建压缩包”及压缩格式,或“封装已有压缩包(保留原密码)”。CLI 示例:

# 多文件 / 目录创建 TAR 组合归档,再封装到图片中(不加密)
./zipveil pack --cover ./cover.webp --format tar.zst --source ./photos --source ./notes.txt -o ./packed.webp
# 单文件压缩流,仅接受一个普通文件
./zipveil pack --cover ./cover.png --format xz --source ./report.pdf -o ./packed.png
# 原样封装已有归档,含已加密目录,无需提供原密码
./zipveil pack --cover ./cover.jpg --archive ./private.7z -o ./packed.jpg
./zipveil pack --cover ./cover.mp4 --archive ./backup.rar -o ./packed.mp4

TAR、GZIP、BZIP2、XZ、Zstandard 及 TAR 组合格式不支持密码加密;封面不会为它们增加加密能力,指定密码会报错。新建 ZIP 封面仍要求密码,普通 ZIP 的密码可选。7Z / RAR 不支持新建,只能封装已有归档;--archive 与 --source、密码选项互斥,保留归档字节和原加密状态。解锁、浏览、勾选提取、检查和批量提取均支持新封装格式。

-source 可重复指定,同时打包多个文件与目录:

./zipveil pack -video ./cover.mp4 -source ./notes.txt -source ./photos -o ./packed.mp4

单个目录保留原有的相对路径布局;多个来源会保留每个目录的顶层名称。重复路径和已选目录内的子路径会去重;归档内路径重名(忽略大小写)时会报错,请先重命名源文件。桌面版的打包、提取和校验支持点击“取消任务”,取消后清理未完成的临时产物;批量提取已完成的目录会保留。

新建 ZIP 时,--compression 支持 deflate(默认,压缩文件内容)和 store(不压缩)。两种模式都会用 AES 加密文件内容。Deflate 模式可用 --level 1 到 --level 9 设置压缩级别,默认 5;数字越大通常压缩率越高、速度越慢。store 模式忽略级别。--aes 可选择 AES-128、AES-192 或 AES-256,默认 256。输出路径若已存在,工具会报错,不会覆盖。符号链接和特殊文件不会被打包;目录条目本身不加密,普通文件内容加密。

打包会记录普通文件和目录的修改时间及普通权限位,写入 UTC 扩展时间字段(精度为 1 秒),并为有效 UTF-8 文件名设置 UTF-8 标记。原始视频或图片前缀字节保持不变,GIF 与 WebP 动画也不会重新编码。

输出先在目标目录内的临时文件中完整写入并同步,然后发布。优先使用硬链接;不支持硬链接时,Windows、macOS、Linux 尝试系统提供的不覆盖重命名;若此方式也不受支持,回退到独占创建目标并复制。所有路径均拒绝覆盖已有文件。最后的复制回退期间目标文件可能短暂可见,失败会清理未完成文件,也需要额外的磁盘空间;请在命令成功退出后使用输出。普通磁盘不受复制回退的额外开销影响。

交互式终端会显示单行动态进度和当前处理文件,例如 [ 42/100%]正在压缩:目录/文件.bin;复制封面时显示封面文件名,进度覆盖封面复制和待压缩文件的读取,压缩结束并成功写入输出文件后才显示 100%。

脚本与标准输入密码

extract、test、extract-all、pack 都支持 --password-stdin,从标准输入读取一行密码,且不显示密码提示。它与 --password 互斥,仅移除行末的 LF 或 CRLF,保留密码首尾空格。没有换行的最后一行也可读取;标准输入为空时明确报错。

./zipveil test ./archive.mp4 --password-stdin < ./password.txt
./zipveil extract ./archive.mp4 -o ./unpacked --password-stdin < ./password.txt
./zipveil extract-all ./archives -o ./unpacked --password-stdin < ./password.txt
./zipveil pack -video ./cover.mp4 -source ./payload -o ./packed.mp4 --password-stdin < ./password.txt

批量模式只读取一行并缓存,作为所有加密归档的共用密码;不同归档密码不同时,继续使用逐个交互式输入。无需密码的文件或已跳过的归档不会消费标准输入。密码文件应自行限制访问权限。命令成功退出码为 0,错误或批量任一失败为 1;重定向输出时不显示动态进度,可直接用于脚本。通过 --password-stdin 传入的密码不进入 ZipVeil 的进程参数。

作为 Go 程序运行

go run . inspect data/demo.mp4
go run . extract data/demo.mp4 -o ./demo-extracted
go run . test data/demo.mp4

这个文件是怎么形成的

新建 ZIP 的封装保留原有尾部追加布局,兼容既有版本:

输出文件 = 原始视频或图片全部字节 + 加密 ZIP 全部字节

其他新建格式和 --archive 原样封装采用带版本的定位尾部:

输出文件 = 原始封面全部字节 + 原始归档全部字节 + 定位元数据 + 固定尾部

定位信息记录归档起点、长度、格式及原文件名,以 CRC32 检测元数据损坏。读取时校验长度、边界和封面,再将归档区间交给相应的纯 Go 读取器,不额外复制整个归档;压缩 TAR 建立索引仍会使用受资源上限约束的临时展开文件。定位 CRC 不提供加密或防篡改认证,完整性能力由归档格式决定。单文件压缩流通过记录的原归档名还原输出名。新建非 ZIP 封面会先生成临时归档,再封装并清理,需预留临时归档空间。

新布局由当前 ZipVeil 识别;旧版 ZipVeil 和其他解压工具不保证能直接读取这些封面文件,仅修改扩展名也不保证可读。原普通归档仍保持标准格式。

MP4 在文件开头保存容器结构和媒体数据。播放器从 MP4 的 ftyp、moov 等 box 读取格式、时间轴和媒体块索引,再按偏移读取 mdat 中的视频与音频数据。当前小示例 data/demo.mp4 共 3,783,589 字节(约 3.6 MiB):MP4 的 ftyp、free、mdat、moov box 在偏移 3,255,418 字节处结束,ZIP 的第一个本地文件头 PK\x03\x04 紧接在该位置。能够接受尾部附加数据的播放器按 MP4 索引播放,ZIP 内容则由归档读取器处理。

JPG、PNG、GIF 与 WebP 的图像内容同样保持原样。能容忍图像结束标记后附加数据的解码器可正常显示封面;严格检查文件尾部的软件可能拒绝打开。

ZIP 则通过文件尾部的 EOCD(End of Central Directory)记录找到中央目录,再由中央目录定位每个文件。如果先生成独立 ZIP 再拼接封面,ZIP 内的偏移通常仍以原 ZIP 起点为基准,读取器需要校正前缀偏移。ZipVeil 创建时用 ZIP writer 的 SetOffset(封面大小) 写出相对于整个输出文件的偏移;两种写法都支持读取。

当前示例包含 1 个加密文件 image.webp,使用 WinZip AES-256、AE-2 和 Deflate 压缩,附加的 ZIP 区段为 528,171 字节。AES 扩展字段 0x9901 记录加密强度及实际压缩方法。AES 加密保护的是条目内容,ZIP 目录中的文件名和部分元数据仍可见。示例密码需要使用创建该文件时设置的密码。

文件扩展名不参与上述结构。改名为 .zip 只是让某些软件选择 ZIP 读取器;字节内容没有因此改变。ZipVeil 可以直接读取视频和图片封装,不需要先改名。

兼容性说明

  • 读取和创建加密 ZIP 使用 gitea.bytevibe.dev/gary/zip;Go 标准库 archive/zip 能读取这种带前缀 ZIP 的目录结构,但不能解密样本的 WinZip AES 条目。
  • 密码编码尝试是为了兼容由不同编码写入 ZIP 密码字节的压缩包。密码错误、编码不匹配或条目损坏都会导致校验失败。
  • 工具提取外层归档;如果外层条目本身是 .zip,它会作为普通文件解压,不会自动递归解压。
  • MP4 中追加数据的兼容性取决于容器结构和播放器。ZipVeil 检查 MP4 的 ftyp 标记,但无法保证所有播放器都接受任意 MP4 后缀数据。
  • 图片封面支持 JPG / JPEG、PNG、GIF、WebP(有损、无损、透明与动画),按图片头识别格式,不重新编码;格式检查不替代对原图完整性的验证。HEIC、AVIF 等暂不作为封面支持。
  • WebP 保留原 RIFF 头及所有图像、透明度、动画数据,归档追加在原容器之后。WebP 容器规范允许读取器忽略尾部数据,但部分软件可能拒绝此类文件。
  • 图片编辑、裁剪、重新压缩,以及聊天或相册平台转码可能移除附加归档。分享时请发送原文件或使用文件传输;文件体积会增加归档及定位信息的大小。

测试

go test ./...

测试覆盖 MP4、JPG、PNG、动态 GIF、WebP 封面往返与图像解码、密码与完整性校验、按需提取、展开资源上限、批量目标冲突策略、ZIP 路径穿越防护、输出发布回退与不覆盖、元数据往返和标准输入密码。

自动发布

推送与 VERSION 一致的 v* 标签触发 .gitea/workflows/release.yml。当前流程使用 ubuntu-latest 标签的 Linux / Docker runner,在 Debian 13 构建容器中安装固定版本 Wails 和 GTK4 / WebKitGTK 开发依赖,测试两个 Go module 与前端,生成 8 个 CLI / 桌面下载包和 SHA-256 清单。

发布使用 runner 的内置 GITEA_TOKEN,工作流请求 contents: write;仓库 Actions 的最大令牌权限也须允许写入 Releases,无需把个人 token 写入源码。先创建草稿,所有附件上传后才发布;失败时保留草稿,可重跑任务。重跑会核对已有附件,拒绝覆盖不一致的内容。

后续发版时同步修改 VERSION、internal/buildinfo/version.go、前端 package / lock 版本及平台包元数据,为该标签添加 docs/releases/v版本号.md,再提交、推送标签。Beta 版本的平台数字版本保持对应的基础版本(如 0.1.0),产品显示完整版本。

S
Description
No description provided
Readme
4.1 MiB
0 Stars 1 Watchers 0 Forks
Languages
Go 63.2%
Vue 15.1%
TypeScript 8.3%
CSS 5.9%
NSIS 3.2%
Other 4.1%