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 LibraryApache Compress Ant Library2026/10/15

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)11gzip bzip2 deflate lz4 lzma snappy xz sevenz pack200 pack200normalize jar
归档任务(taskdef)4tar zip cpio ar
解压任务(taskdef)16gunzip bunzip2 undeflate unlz4 unlzma unsnappy unxz un7z unar unarj uncpio undump untar unzip unz unbrotli
条目类型(typedef)7zipentry tarentry arjentry arentry cpioentry dumpentry sevenzentry
文件集类型(typedef)7zipfileset tarfileset arjfileset arfileset cpiofileset dumpfileset sevenzfileset
资源类型(typedef)11gzipresource bzip2resource deflateresource lz4resource lzmaresource pack200resource snappyresource xzresource zresource brotliresource devzero
批量资源(typedef)1archives
条件(componentdef)5hasusername 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 路径绑定的关键。

十、参考链接

  1. Apache Compress Antlib 官方手册(打包/解包/归档任务参数):https://ant.apache.org/antlibs/compress/
  2. 包内离线手册:apache-ant-compress-1.5/docs/(pack.html、unpack.html、archive.html、archives.html)
  3. Maven 中央仓库坐标页:https://repo1.maven.org/maven2/org/apache/ant/ant-compress/
  4. Apache Commons Compress 格式说明:https://commons.apache.org/proper/commons-compress/
  5. 系列前篇:《Apache Compress Ant Library 1.5 快速入门与集成示例》