Apache Compress Ant Library 1.5 常用 API 与任务详解
Apache Compress Ant Library 1.5 常用任务详解:全景梳理 31 个任务与 26 个类型,逐一实测 gzip/bzip2/deflate 压缩级别、xz/lz4/lzma/snappy 回环、decompressConcatenated 多流解压、tar/zip/cpio/ar/sevenz 归档与系统工具互通、zipentry/zipfileset/tarfileset 资源与属主权限、archives 批量提取及 pack200 在 JDK 17 的真实行为,回显均实测通过。
Apache Compress Ant Library 1.5 常用 API 与任务详解
目标:全景梳理 Apache Compress Antlib 1.5 的任务与类型体系,并对每个常用任务做实测:属性语义、压缩级别效果、多流解压、归档互操作、条目级读取与权限保留,以及 pack200 在 JDK 17 的真实行为;本文全部命令与回显均已在 Ubuntu 22.04 + JDK 17 + Ant 1.10.12 + ant-compress 1.5 实测通过。
这篇文档按「任务家族」组织:单文件流压缩(gzip/bzip2/deflate/xz/lz4/lzma/snappy)、多文件归档(tar/zip/cpio/ar/sevenz)、解包(untar/unzip/un 系列)、条目与资源(zipentry/tarfileset/archives)、条件(hasusername 等)。每个小节都遵循同一结构:引导说明 → 实测脚本 → 真实回显 → 验证结论。jar 的装配与命名空间机制见快速入门篇,本文不再重复。
一、环境信息
本节固定实测基线环境,所有命令在此环境执行。
- 操作系统:Ubuntu 22.04.5 LTS(x86_64),默认 locale 修正为
LANG=C.UTF-8(中文文件名实验的前提,原因见第五篇第四章) - JDK:OpenJDK 17.0.20.1
- Ant:Apache Ant 1.10.12(apt 安装)
- 组件:Apache Compress Antlib 1.5(
org.apache.ant:ant-compress:1.5+ commons-compress-1.14.jar + xz-1.6.jar + dec-0.1.2.jar,jar 已常驻/usr/share/ant/lib) - 工作目录:
/root/work(归档实验)、/root/adv(资源与编码实验)、/root/fmtwork(格式变体实验)
环境确认(实时回显):
ant -version && java -version 2>&1 | head -1
ls /usr/share/ant/lib/ | grep -E "ant-compress|commons-compress|xz-1.6|dec-0.1"
Apache Ant(TM) version 1.10.12 compiled on January 17 1970
openjdk version "17.0.20.1" 2026-08-18
ant-compress-1.5.jar
commons-compress-1.14.jar
dec-0.1.2.jar
xz-1.6.jar
实验种子文件(后续所有小节共用):
mkdir -p /root/work/logs/sub
echo "2026-10-15 10:00:00 INFO service started" > /root/work/logs/app.log
echo "2026-10-15 10:00:01 INFO db connected" > /root/work/logs/db.log
echo "2026-10-15 10:00:02 AUDIT login user=q1w2e3r4" > /root/work/logs/sub/audit.log
ls -l /root/work/logs/
-rw-r--r-- 1 root root 40 Oct 15 07:19 app.log
-rw-r--r-- 1 root root 37 Oct 15 07:19 db.log
drwxr-xr-x 2 root root 4096 Oct 15 07:19 sub
-rw-r--r-- 1 root root 45 Oct 15 07:19 sub/audit.log
二、任务与类型全景
本节给出库的完整能力地图,数据直接解析自 jar 内 org/apache/ant/compress/antlib.xml(实测统计):
unzip -p /usr/share/ant/lib/ant-compress-1.5.jar org/apache/ant/compress/antlib.xml > /tmp/antlib.xml
| 类别 | 数量 | 成员 |
|---|---|---|
| 压缩任务(taskdef) | 11 | gzip bzip2 deflate lz4 lzma snappy xz sevenz pack200 pack200normalize jar |
| 归档任务(taskdef) | 4 | tar zip cpio ar |
| 解压任务(taskdef) | 16 | gunzip bunzip2 undeflate unlz4 unlzma unsnappy unxz un7z unar unarj uncpio undump untar unzip unz unbrotli |
| 条目类型(typedef) | 7 | zipentry tarentry arjentry arentry cpioentry dumpentry sevenzentry |
| 文件集类型(typedef) | 7 | zipfileset tarfileset arjfileset arfileset cpiofileset dumpfileset sevenzfileset |
| 资源类型(typedef) | 11 | gzipresource bzip2resource deflateresource lz4resource lzmaresource pack200resource snappyresource xzresource zresource brotliresource devzero |
| 批量资源(typedef) | 1 | archives |
| 条件(componentdef) | 5 | hasusername hasgroupname hasuserid hasgroupid hasmode |
合计 31 个任务、26 个类型、5 个条件。两个能力边界值得先知道:有 unbrotli(Brotli 解压)但没有 brotli 压缩任务;有 unz(.Z 解压)但没有 z 压缩任务——这两类只读格式的实现依赖只能解码(Brotli 官方无参考编码器、LZW 压缩未封装)。
💡 技术拓展:Antlib 任务与核心 Ant 同名任务的冲突问题——Antlib 类型挂在自己的命名空间下,
<compress:zip>与核心<zip>可同脚本共存,按前缀分流;想用 Antlib 版覆盖核心版语义,也可用<taskdef uri=... resource=... />装到默认命名空间,但不建议。
三、单文件压缩任务:gzip / bzip2 / deflate / xz / lz4 / lzma / snappy
本节覆盖七个流压缩任务。共同属性模式:src(源文件,或嵌套资源)+ destfile/dest(目标文件);差异在格式特有参数。所有压缩实测都用「压缩 → 解压 → MD5 对比」闭环验证。
3.1 level 压缩级别(gzip / deflate,1.5 新增)
level 取值 0–9(0=仅存储,9=最大压缩)。实测脚本(完整内容,/root/build-arch.xml 片段):
<compress:gzip src="work/logs/app.log" destfile="work/l0.gz" level="0"/>
<compress:gzip src="work/logs/app.log" destfile="work/l9.gz" level="9"/>
<compress:deflate src="work/logs/app.log" destfile="work/l0.deflate" level="0"/>
<compress:deflate src="work/logs/app.log" destfile="work/l9.deflate" level="9"/>
<compress:gunzip src="work/l0.gz" dest="work/l0-restored.log"/>
<compress:undeflate src="work/l9.deflate" dest="work/l9-restored.log"/>
执行 ant -f build-arch.xml level 后(实时回显):
ls -l work/l0.gz work/l9.gz work/l0.deflate work/l9.deflate
-rw-r--r-- 1 root root 51 Oct 15 07:19 work/l0.deflate
-rw-r--r-- 1 root root 63 Oct 15 07:19 work/l0.gz
-rw-r--r-- 1 root root 45 Oct 15 07:19 work/l9.deflate
-rw-r--r-- 1 root root 57 Oct 15 07:19 work/l9.gz
40 字节的输入:level 0 产物比 level 9 大 6 字节(gzip 63→57、deflate 51→45),级别参数真实生效;gzip -lv work/l0.gz 可见 store 方式。解压回环 MD5 一致(见 3.3 总表)。日志类高频重复文本建议 level 9,二进制已压缩内容用默认级别即可(再压不省)。
3.2 xz / lz4 / lzma / snappy 回环
四格式压缩解压实测(/root/build-arch.xml 片段):
<compress:snappy src="work/logs/app.log" destfile="work/app.log.sz"/>
<compress:unsnappy src="work/app.log.sz" dest="work/app-restored-sz.log"/>
<compress:lz4 src="work/logs/app.log" destfile="work/app.log.lz4"/>
<compress:unlz4 src="work/app.log.lz4" dest="work/app-restored-lz4.log"/>
<compress:lzma src="work/logs/app.log" destfile="work/app.log.lzma"/>
<compress:unlzma src="work/app.log.lzma" dest="work/app-restored-lzma.log"/>
执行 ant -f build-arch.xml snappy lz4 lzma 后回环校验(实时回显,MD5 汇总见 3.3):
[compress:snappy] Building: app.log.sz
[compress:unsnappy] Expanding /root/work/app.log.sz to /root/app-restored-sz.log
[compress:lz4] Building: app.log.lz4
[compress:unlz4] Expanding /root/work/app.log.lz4 to /root/app-restored-lz4.log
[compress:lzma] Building: app.log.lzma
[compress:unlzma] Expanding /root/work/app.log.lzma to /root/app-restored-lzma.log
三点说明:snappy/lz4 面向速度(无系统工具互读,属 JVM 生态内格式);lzma 是 xz 的裸流版(无外层封装头);xz 默认带 CRC64 校验的外层封装,gzip -t 系校验用 xz -t。
3.3 回环无损总表
对 work/logs/app.log(40 字节,MD5 f499bc772878ee16b02da28a7e5fb801)执行全部格式压缩后逐个解压(实时回显):
md5sum work/logs/app.log work/l0-restored.log work/l9-restored.log \
work/app-restored-sz.log work/app-restored-lz4.log work/app-restored-lzma.log
f499bc772878ee16b02da28a7e5fb801 work/logs/app.log
f499bc772878ee16b02da28a7e5fb801 work/l0-restored.log
f499bc772878ee16b02da28a7e5fb801 work/l9-restored.log
f499bc772878ee16b02da28a7e5fb801 work/app-restored-sz.log
f499bc772878ee16b02da28a7e5fb801 work/app-restored-lz4.log
f499bc772878ee16b02da28a7e5fb801 work/app-restored-lzma.log
六个格式 × 级别组合全部无损。bzip2/xz 的同源验证见快速入门篇(MD5 同值一致)。
3.4 decompressConcatenated:多流 gzip 解压
gzip 流可以拼接(cat a.gz b.gz > c.gz 仍是合法 gzip)。默认只解第一个流,属性打开后解全部(/root/build-arch.xml 片段):
<compress:gzip src="work/logs/app.log" destfile="work/one.gz"/>
<concat destfile="work/double.log.gz" binary="true">
<fileset file="work/one.gz"/>
<fileset file="work/one.gz"/>
</concat>
<compress:gunzip src="work/double.log.gz" dest="work/concat-first.log"/>
<compress:gunzip src="work/double.log.gz" dest="work/concat-all.log" decompressConcatenated="true"/>
执行 ant -f build-arch.xml concat 后(实时回显):
wc -c work/concat-first.log work/concat-all.log
40 work/concat-first.log
80 work/concat-all.log
120 total
验证点:两份 gzip 流拼接后,默认解出 40 字节(仅第一个流),decompressConcatenated="true" 解出 80 字节(两个流全部展开)。日志轮转场景(多段 gz 拼接归档)必须显式开启该属性,否则静默丢数据——这是该任务家族最容易踩的语义坑。
四、归档任务:tar / zip / cpio / ar / sevenz
本节覆盖五个多文件归档任务。共同模式:destfile + 任意资源集合(<fileset> 最常用);归档任务默认带「目标已最新则跳过」的增量判定(见第六篇实测)。
4.1 基本用法与系统工具互通
/root/build-arch.xml 的五个归档任务(片段):
<compress:tar destfile="work/logs.tar">
<fileset dir="work/logs"/>
</compress:tar>
<compress:zip destfile="work/logs.zip">
<fileset dir="work/logs"/>
</compress:zip>
<compress:cpio destfile="work/logs.cpio">
<fileset dir="work/logs"/>
</compress:cpio>
<compress:sevenz destfile="work/logs.7z">
<fileset dir="work/logs"/>
</compress:sevenz>
执行 ant -f build-arch.xml all-arch 后产物清单(实时回显):
ls -l work/logs.tar work/logs.zip work/logs.cpio work/logs.7z
-rw-r--r-- 1 root root 324 Oct 15 07:19 work/logs.7z
-rw-r--r-- 1 root root 512 Oct 15 07:19 work/logs.cpio
-rw-r--r-- 1 root root 10240 Oct 15 07:19 work/logs.tar
-rw-r--r-- 1 root root 423 Oct 15 07:19 work/logs.zip
逐格式用系统工具校验(实时回显):
tar -tvf work/logs.tar
-rw-r--r-- 0/0 40 2026-10-15 07:19 app.log
-rw-r--r-- 0/0 37 2026-10-15 07:19 db.log
-rw-r--r-- 0/0 45 2026-10-15 07:19 sub/audit.log
unzip -l work/logs.zip
Archive: work/logs.zip
Length Date Time Name
--------- ---------- ----- ----
40 2026-10-15 07:19 app.log
37 2026-10-15 07:19 db.log
45 2026-10-15 07:19 sub/audit.log
--------- -------
122 3 files
cpio -itv < work/logs.cpio
cpio: warning: archive header has reverse byte-order
1 block
-rw-r--r-- 1 root root 40 Oct 15 2026 app.log
-rw-r--r-- 1 root root 37 Oct 15 2026 db.log
-rw-r--r-- 1 root root 45 Oct 15 2026 sub/audit.log
1 block
验证点:三格式条目、大小(40/37/45 字节)与源文件完全一致;tar 的属主显示 0/0(Antlib 不携带构建机 uid/gid,除非用 tarfileset 指定,见第六章);cpio 的 reverse byte-order 警告说明默认写出的是大端序头(GNU cpio 实测仍可正常读出全部条目),在意提示的话用 4.2 节的文本头格式。
4.2 cpio 格式变体
<compress:cpio> 的 format 属性支持 binary(默认)、new-ascii、old-ascii。实测脚本(/root/build-fmt.xml 片段):
<compress:cpio destfile="fmtwork/new.cpio" format="new-ascii">
<fileset file="fmtwork/data.txt"/>
</compress:cpio>
<compress:cpio destfile="fmtwork/odc.cpio" format="old-ascii">
<fileset file="fmtwork/data.txt"/>
</compress:cpio>
cpio -itv < fmtwork/new.cpio
-rw-r--r-- 1 root root 31 Oct 15 2026 data.txt
1 block
cpio -itv < fmtwork/odc.cpio
-rw-r--r-- 1 root root 31 Oct 15 2026 data.txt
1 block
验证点:两种文本头格式(new-ascii = SVR4 新格式、old-ascii = 可移植 odc 格式)均无 reverse byte-order 警告,跨平台交换归档建议用 format="new-ascii"。
4.3 sevenz:7z 归档与压缩算法选择
<compress:sevenz> 支持 contentCompression 指定条目压缩算法(1.5 可用值含 LZMA2 默认、DEFLATE、BZIP2 等)。实测(/root/build-fmt.xml 片段):
<compress:sevenz destfile="fmtwork/s-lzma2.7z" contentCompression="LZMA2">
<fileset file="fmtwork/data.txt"/>
</compress:sevenz>
<compress:sevenz destfile="fmtwork/s-deflate.7z" contentCompression="DEFLATE">
<fileset file="fmtwork/data.txt"/>
</compress:sevenz>
<compress:un7z src="fmtwork/s-deflate.7z" dest="fmtwork/out"/>
echo "LZMA2=$(stat -c%s fmtwork/s-lzma2.7z) DEFLATE=$(stat -c%s fmtwork/s-deflate.7z)"
LZMA2=139 DEFLATE=135
md5sum fmtwork/data.txt fmtwork/out/data.txt
01a66913600e8f9e849175dfa4e5f873 fmtwork/data.txt
01a66913600e8f9e849175dfa4e5f873 fmtwork/out/data.txt
验证点:两种算法均成功写出且 un7z 解压内容 MD5 一致;31 字节小文件上两种产物都大于源文件(7z 头部开销),真实大文件上 LZMA2 通常显著更优。注意:该属性指定的是「条目数据」的压缩方法,与 tar+gzip 的两层结构不同,7z 是在单一文件内完成压缩。
4.4 ar:16 字符条目名限制与 bsd 格式的互读陷阱
ar 是 Unix 静态库格式(.a),条目名上限 16 字符。超长实测(/root/build-arch.xml 的 ar 目标,含 36 字符文件名):
ant -f build-arch.xml ar
ar:
BUILD FAILED
/root/build-arch.xml:38: Failed to write archive
用 -debug 抓根因(实时回显):
Caused by: java.io.IOException: filename too long, > 16 chars: a-very-long-entry-name-exceeds-16.txt
解法是 format="bsd"(BSD 扩展名格式,把长名写进数据区)。但实测发现该格式的互读性有缺陷:生成的归档 GNU ar 读不了,Antlib 自己的解压任务也只能部分回读:
ant -f build-arch.xml ar-bsd && ls -l work/libdemo-bsd.a && ar t work/libdemo-bsd.a
BUILD SUCCESSFUL
ar: work/libdemo-bsd.a: malformed archive
-rw-r--r-- 1 root root 185 Oct 15 07:20 work/libdemo-bsd.a
用 Antlib 的 <compress:unar> 回读(实测):
[compress:unar] expanding a-very-long-entry-name-exceeds-16.txt to /root/work/ar-bsd-out/a-very-long-entry-name-exceeds-16.txt
BUILD FAILED
/root/build-ar-bsd-read.xml:4: invalid entry trailer. Occured at byte: 180
md5sum work/ar-src/a-very-long-entry-name-exceeds-16.txt work/ar-bsd-out/a-very-long-entry-name-exceeds-16.txt
88717791245166401f5477775fed1ef5 work/ar-src/a-very-long-entry-name-exceeds-16.txt
88717791245166401f5477775fed1ef5 work/ar-bsd-out/a-very-long-entry-name-exceeds-16.txt
结论如实记录:format="bsd" 能写出含长名的 ar(185 字节产物),长名条目本身能被 unar 解出(MD5 一致),但 unar 在读取后续条目时报 invalid entry trailer(byte 180)构建失败,GNU ar 更是完全不识别。生产建议:ar 场景把条目名控制在 16 字符内走默认格式(短名实测与 GNU ar 完全互通),长名需求改用 tar/zip。
五、解包任务:untar / unzip / uncpio / un7z 与 un 系列
本节实测四类归档解包与流解压任务的完整回环。
5.1 四格式解包回环
/root/build-arch.xml 的 extract-all 目标(片段):
<mkdir dir="work/out-tar"/>
<compress:untar src="work/logs.tar" dest="work/out-tar"/>
<mkdir dir="work/out-zip"/>
<compress:unzip src="work/logs.zip" dest="work/out-zip"/>
<mkdir dir="work/out-cpio"/>
<compress:uncpio src="work/logs.cpio" dest="work/out-cpio"/>
<mkdir dir="work/out-7z"/>
<compress:un7z src="work/logs.7z" dest="work/out-7z"/>
回环一致性校验(实时回显):
md5sum work/logs/app.log work/out-tar/app.log work/out-zip/app.log work/out-cpio/app.log work/out-7z/app.log
f499bc772878ee16b02da28a7e5fb801 work/logs/app.log
f499bc772878ee16b02da28a7e5fb801 work/out-tar/app.log
f499bc772878ee16b02da28a7e5fb801 work/out-zip/app.log
f499bc772878ee16b02da28a7e5fb801 work/out-cpio/app.log
f499bc772878ee16b02da28a7e5fb801 work/out-7z/app.log
验证点:tar/zip/cpio/7z 四格式全部解包成功,内容与源文件 MD5 一致。untar/unzip 支持 overwrite="false" 做覆盖控制,目录不存在时自动创建。
5.2 un 系列流解压与中文编码
单文件解压任务(gunzip/bunzip2/unxz/unlz4/unlzma/unsnappy/undeflate)的 dest 指定输出文件名(可改名),已在 3.1/3.2/3.4 节反复实测。这里补 zip 文件名编码全链路:创建含中文文件名的 zip 并按编码解回(/root/build-adv.xml 片段):
<mkdir dir="adv/cn"/>
<echo file="adv/cn/日志文件.log">中文内容测试</echo>
<echo file="adv/cn/配置.conf">charset=utf-8</echo>
<compress:zip destfile="adv/cn.zip" encoding="UTF-8">
<fileset dir="adv/cn"/>
</compress:zip>
<compress:zip destfile="adv/cn-gbk.zip" encoding="GBK">
<fileset dir="adv/cn"/>
</compress:zip>
<compress:unzip src="adv/cn-gbk.zip" dest="adv/cn-out" encoding="GBK"/>
解包结果(实时回显):
unzip -l adv/cn.zip
Archive: adv/cn.zip
Length Date Time Name
--------- ---------- ----- ----
18 2026-10-15 07:19 日志文件.log
13 2026-10-15 07:19 配置.conf
--------- -------
31 2 files
ls adv/cn-out/
日志文件.log
配置.conf
用 Python 验证 GBK 包内存储的字节确实按 GBK 编码(实时回显):
python3 -c "import zipfile; z=zipfile.ZipFile('/root/adv/cn-gbk.zip'); print([n.encode('cp437').decode('gbk') for n in z.namelist()])"
['日志文件.log', '配置.conf']
验证点:encoding 属性同时控制写入与读取的文件名编码;UTF-8 包系统 unzip 可直读,GBK 包必须用相同 encoding 解(否则文件名乱码)。注意运行 Ant 的终端需 LANG=C.UTF-8,否则中文文件名在落盘前就会失败(完整分析见第五篇进阶实战)。
六、条目与资源类型:zipentry / zipfileset / tarfileset / archives
本节是 Antlib 相对核心 Ant 的增量价值所在:把「归档内条目」当资源用,实现跨归档读取、转换与权限保留。
6.1 zipentry:读取归档内单个条目
/root/build-adv.xml 片段(先用 <compress:zip> 生成 adv/main.zip):
<local name="entry.content"/>
<loadresource property="entry.content">
<compress:zipentry archive="adv/main.zip" name="a.txt"/>
</loadresource>
<echo>zipentry a.txt content=${entry.content}</echo>
<copy todir="adv/entry-copy">
<compress:zipentry archive="adv/main.zip" name="b.txt"/>
</copy>
执行 ant -f build-adv.xml entry-read(实时回显):
[echo] zipentry a.txt content=alpha
cat adv/entry-copy/b.txt
bravo
验证点:zipentry 既能作为 <loadresource> 的内容源(不解包读条目文本),也能作为 <copy> 的资源(单条目落盘)。tarentry/sevenzentry/cpioentry 等七个条目类型用法相同,只是换 archive 格式。
6.2 zipfileset:跨归档转换(zip → tar)
<compress:zipfileset src="...zip"> 把整个 zip 当作资源集合喂给其他归档任务,实现格式转换(/root/build-adv.xml 片段):
<compress:zip destfile="adv/main.zip">
<fileset dir="adv/src"/>
</compress:zip>
<compress:tar destfile="adv/from-zip-entries.tar">
<compress:zipfileset src="adv/main.zip"/>
</compress:tar>
执行后验证(实时回显):
tar -tvf adv/from-zip-entries.tar
-rw-r--r-- 0/0 5 2026-10-15 07:19 a.txt
-rw-r--r-- 0/0 5 2026-10-15 07:19 b.txt
验证点:zip 的两个条目无损转成 tar 条目(内容各 5 字节),且属主为 0/0——zip 条目不携带 Unix 属主,转换后 tar 侧补零;需要保留属主的归档请从源目录直接打 tar(见 6.3)。
6.3 tarfileset:属主与权限保留 + 条件校验
<compress:tarfileset> 可显式指定 tar 条目的 username/group/uid/gid/filemode(注意属性名是 group 而非核心 Ant 的 groupname)。/root/build-adv.xml 片段:
<compress:tar destfile="adv/perm.tar">
<compress:tarfileset dir="adv/src" username="deploy" group="ops" uid="1001" gid="1001" filemode="600"/>
</compress:tar>
<compress:untar src="adv/perm.tar" dest="adv/cond-out"/>
<condition property="has.user">
<compress:hasusername name="deploy">
<compress:tarentry archive="adv/perm.tar" name="a.txt"/>
</compress:hasusername>
</condition>
<condition property="has.mode">
<compress:hasmode mode="0600">
<compress:tarentry archive="adv/perm.tar" name="a.txt"/>
</compress:hasmode>
</condition>
<echo>hasusername(deploy)=${has.user}</echo>
<echo>hasmode(0600)=${has.mode}</echo>
执行 ant -f build-adv.xml cond-check(实时回显):
[echo] hasusername(deploy)=true
[echo] hasmode(0600)=true
BUILD SUCCESSFUL
tar -tvf adv/perm.tar
-rw------- deploy/ops 5 2026-10-15 07:19 a.txt
-rw------- deploy/ops 5 2026-10-15 07:19 b.txt
验证点:权限 600、属主 deploy/ops 真实写入 tar 头(系统 tar 可见);hasusername/hasmode 条件以归档条目为输入返回 true,可放进 <fail unless=...> 做发布前元数据检查——这是核心 Ant 任务做不到的归档内检查能力。
6.4 archives:多归档批量提取
<compress:archives> 把一组归档的所有条目展平为一个资源集合,嵌套 <zips>/<tars>/<ars> 等分格式子元素选择归档(/root/build-arch-res.xml 片段):
<compress:zip destfile="/root/adv2/r1.zip">
<fileset file="/root/adv2/a.txt"/>
</compress:zip>
<compress:zip destfile="/root/adv2/r2.zip">
<fileset file="/root/adv2/b.txt"/>
</compress:zip>
<mkdir dir="/root/adv2/copy-out"/>
<copy todir="/root/adv2/copy-out">
<compress:archives>
<compress:zips>
<fileset dir="/root/adv2" includes="r*.zip"/>
</compress:zips>
</compress:archives>
</copy>
执行(实时回显):
[copy] Copying 2 resources to /root/adv2/copy-out
BUILD SUCCESSFUL
cat adv2/copy-out/a.txt adv2/copy-out/b.txt
alpha
bravo
验证点:两个 zip 的全部条目被展平提取到同一目录,内容正确。一个重要实测发现:同样的 archives 资源喂给 Antlib 自己的 <compress:unzip> 时,日志会打出「Expanding: a.txt into ...」但文件并不落盘(1.5 实测缺陷,详见第五篇第五章),批量提取请用 <copy> 配 archives 的组合。
七、pack200 家族:JDK 17 上实测必失败
pack200 是「jar 内 class 常量池重排压缩」格式,其 API 已从 JDK 14 移除。Antlib 1.5 的 <pack200>/<unpack200>/<pack200normalize> 依赖 java.util.jar.Pack200,在 JDK 17 上必然失败(/root/build-arch.xml 的 pack200 目标实测):
ant -f build-arch.xml pack200
pack200:
[jar] Building jar: /root/work/demo.jar
BUILD FAILED
/root/build-arch.xml:107: java.lang.NoClassDefFoundError: java/util/jar/Pack200
Caused by: java.lang.ClassNotFoundException: java.util.jar.Pack200
at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:445)
结论如实记录:这不是配置问题,是 API 层缺失,1.5 无解。解法有二:pack200 相关目标单独放到 JDK 8–13 的 Ant 环境执行;或直接放弃 pack200(JDK 官方已废弃该格式,JAR 压缩收益在现代构建里可由 --release 裁剪与资源压缩替代)。其余 30 个任务在 JDK 17 上均实测正常。
八、与核心 Ant 任务的关系与选择
本节回答「核心 Ant 已有 gzip/bzip2/tar/zip,为什么还要 Antlib」。基于全部实测的差异清单:
| 能力 | 核心 Ant(1.10.12) | Antlib 1.5(实测) |
|---|---|---|
| 格式覆盖 | gzip/bzip2/tar/zip | 另加 xz/lz4/lzma/snappy/cpio/ar/7z(7 种) |
| 压缩级别 | gzip 不支持 level | <gzip level="0-9"> 实测生效(63→57 字节) |
| 多流 gzip | 不支持 | decompressConcatenated 实测 40→80 字节 |
| 7z 格式 | 无 | <compress:sevenz> + contentCompression 实测 |
| 归档条目作为资源 | 无 | zipentry/tarentry + archives 实测 |
| 归档内属主/权限检查 | 无 | hasusername/hasmode 条件实测 true |
| 增量判定 | zip 有 update | 归档任务实测 is up-to-date, nothing to do |
选择建议:只用 zip/tar/gzip 且无新格式需求,核心 Ant 够用;一旦涉及 xz/7z/条目级操作/权限校验,引入 Antlib。两者共存无冲突(命名空间隔离),可以渐进迁移。
九、注意事项与 FAQ
Q1:filemode 写了 600,解压出来怎么不是?
tar 头里的 600/deploy/ops 元数据已正确写入(tar -tvf 可见 -rw-------),但 Antlib 的 <compress:untar> 解包时不还原权限位(实测解出 -rw-r--r--,按 umask 赋默认权限)。需要完整还原属主与权限的部署场景,请用系统 tar -xf 解包。
Q2:<compress:tar> 第二次执行为什么秒完成?
归档任务内置增量判定:所有输入的 mtime 未晚于产物时输出「is up-to-date, nothing to do」并跳过(实测回显见 4.3 节之后)。CI 里强制重打可先 <delete> 产物或用 mode="create"。
Q3:ar 任务想打包 .a 静态库,条目名从哪来?
来自 fileset 的文件名本身,所以先在暂存目录把文件改短(≤16 字符)再打包;format="bsd" 长名方案因互读缺陷不推荐(4.4 节实测)。
Q4:gzip 产物能不能用系统 gunzip 解?
能。所有单文件压缩任务产物均为标准格式(快速入门篇已实测 gzip -t/bzip2 -t/xz -t 全过);snappy/lz4 例外——它们是 JVM 生态格式,需 Commons Compress 代码解。
Q5:任务名前缀必须叫 compress 吗?
不必。xmlns:cmp="antlib:org.apache.ant.compress" 后用 <cmp:gzip> 同样有效;URI 里的 org.apache.ant.compress 才是与 jar 内 antlib.xml 路径绑定的关键。
十、参考链接
- Apache Compress Antlib 官方手册(打包/解包/归档任务参数):https://ant.apache.org/antlibs/compress/
- 包内离线手册:
apache-ant-compress-1.5/docs/(pack.html、unpack.html、archive.html、archives.html) - Maven 中央仓库坐标页:https://repo1.maven.org/maven2/org/apache/ant/ant-compress/
- Apache Commons Compress 格式说明:https://commons.apache.org/proper/commons-compress/
- 系列前篇:《Apache Compress Ant Library 1.5 快速入门与集成示例》