Archivers and Compressors(归档和压缩)

Commons Compress 调用压缩单个数据压缩格式流的所有格式,而在单个(可能压缩的)存档中收集多个条目的所有格式都是存档格式。

支持的压缩格式有 gzip、bzip2、xz、lzma、Pack200、DEFLATE、Brotli、DEFLATE64、ZStandard 和 Z,归档格式有 7z、ar、arj、cpio、dump、tar 和 zip。 Pack200 是一种特殊情况,因为它只能压缩 JAR 文件。

我们目前只提供对 arj、dump、Brotli、DEFLATE64 和 Z 的读取支持。arj 只能读取未压缩的档案,7z 可以读取具有 7z 支持的许多压缩和加密算法的档案,但在写入档案时不支持加密。

Buffering

流类都封装了调用代码提供的流,它们直接处理它们而无需任何额外的缓冲。 另一方面,他们中的大多数人将从缓冲中受益,因此强烈建议用户在使用 Commons Compress API 之前将他们的流包装在 Buffered(In|Out)putStreams 中。

Factories

Compress 提供工厂方法来根据压缩器或归档器格式的名称创建输入/输出流,以及尝试猜测输入流格式的工厂方法。

要使用算法名称创建写入给定输出的压缩器:

  1. CompressorOutputStream gzippedOut = new CompressorStreamFactory()
  2. .createCompressorOutputStream(CompressorStreamFactory.GZIP, myOutputStream);

让工厂猜测给定归档器流的输入格式:

  1. ArchiveInputStream input = new ArchiveStreamFactory()
  2. .createArchiveInputStream(originalInput);

让工厂猜测给定压缩器流的输入格式:

  1. CompressorInputStream input = new CompressorStreamFactory()
  2. .createCompressorInputStream(originalInput);

请注意,无法检测 lzma 或 Brotli 格式,因此只能使用两个 arg 版本的 createCompressorInputStream。 在 Compress 1.9 之前,.Z 格式也没有被自动检测到。

Restricting Memory Usage

从 Compress 1.14 开始,CompressorStreamFactory 有一个可选的构造函数参数,可用于设置在解压缩或压缩流时可以使用的内存上限。 从 1.14 开始,此设置仅影响解压缩 Z、XZ 和 LZMA 压缩流。

由于 Compress 1.19 SevenZFile 还有一个可选的构造函数来传递内存上限,支持的是 LZMA 压缩流。 从 Compress 1.21 开始,在读取存档的元数据时也会考虑此设置。

对于 Snappy 和 LZ4 格式,压缩期间使用的内存量与窗口大小成正比。

Statistics

从 Compress 1.17 开始,大多数 CompressorInputStream 实现以及 ZipArchiveInputStream 和 ZipFile.getInputStream 返回的所有流都实现了 InputStreamStatistics 接口。 SevenZFile 通过 getStatisticsForCurrentEntry 方法提供当前条目的统计信息。 此接口可用于在提取流时跟踪进度,或在压缩比变得非常大时检测潜在的 zip 炸弹。

Archivers

Unsupported Features

许多受支持的格式已经开发了不同的方言和扩展名,并且某些格式允许 Commons Compress (尚不)支持的功能.

ArchiveInputStream 类提供了一个 canReadEntryData 方法,如果 Commons Compress 可以检测到存档使用当前实现不支持的功能,该方法将返回 false。 如果它返回 false,您不应该尝试阅读该条目,而是跳过它。

Entry Names

所有归档格式都通过 ArchiveEntry 的实例(或更确切地说是它的子类)提供有关各个归档条目的元数据。 从档案读取时,getName 方法提供的信息是存储在档案内的原始名称。 根本无法保证该名称代表相对文件名,甚至是目标操作系统上的有效文件名。 当您尝试从条目名称创建文件名时,您应该仔细检查结果。

Common Extraction Logic(通用提取逻辑)

除了 7z 之外,所有格式都提供了 ArchiveInputStream 的子类,可用于创建存档。 对于 7z,SevenZFile 提供了一个类似的 API,它不代表流,因为我们的实现需要随机访问输入并且不能用于一般流。 ZIP 实现也可以从随机访问中受益匪浅,有关详细信息,请参阅 zip 页面。

假设您要将存档提取到您将调用 getNextEntry 的目标目录,验证该条目是否可以读取,从该条目的名称构造一个合理的文件名,创建一个文件并将所有内容写入其中 - 这里 IOUtils.copy 可能会出现便利。 您对每个条目都这样做,直到 getNextEntry 返回 null。

大概架构可能看起来像:

  1. File targetDir = ...
  2. try (ArchiveInputStream i = ... create the stream for your format, use buffering...) {
  3. ArchiveEntry entry = null;
  4. while ((entry = i.getNextEntry()) != null) {
  5. if (!i.canReadEntryData(entry)) {
  6. // log something?
  7. continue;
  8. }
  9. String name = fileName(targetDir, entry);
  10. File f = new File(name);
  11. if (entry.isDirectory()) {
  12. if (!f.isDirectory() && !f.mkdirs()) {
  13. throw new IOException("failed to create directory " + f);
  14. }
  15. } else {
  16. File parent = f.getParentFile();
  17. if (!parent.isDirectory() && !parent.mkdirs()) {
  18. throw new IOException("failed to create directory " + parent);
  19. }
  20. try (OutputStream o = Files.newOutputStream(f.toPath())) {
  21. IOUtils.copy(i, o);
  22. }
  23. }
  24. }
  25. }

其中假设的 fileName 方法由您编写,并提供将要写入磁盘的文件的绝对名称。 在这里,您应该执行检查以确保生成的文件名实际上是操作系统上的有效文件名,或者在使用条目名称作为输入时属于 targetDir 内的文件。

如果你想将存档格式与压缩格式结合起来——比如在读取“tar.gz”文件时——你可以将 ArchiveInputStream 包裹在 CompressorInputStream 里面,例如:

  1. try (InputStream fi = Files.newInputStream(Paths.get("my.tar.gz"));
  2. InputStream bi = new BufferedInputStream(fi);
  3. InputStream gzi = new GzipCompressorInputStream(bi);
  4. ArchiveInputStream o = new TarArchiveInputStream(gzi)) {
  5. }

Common Archival Logic(通用归档逻辑)

除了 7z 之外,所有支持写入的格式都提供了 ArchiveOutputStream 的子类,可用于创建存档。对于 7z,SevenZOutputFile 提供了一个类似的 API,它不代表流,因为我们的实现需要随机访问输出并且不能用于一般流。 ZipArchiveOutputStream 类也将受益于随机访问,但可用于不可搜索的流 - 但并非所有功能都可用并且存档大小可能略大,请参阅 zip 页面了解详细信息。

假设您想将一组文件添加到存档中,您可以首先对每个文件使用 createArchiveEntry。通常,这将基于 File 或 Path 实例设置一些标志(通常是最后修改时间、大小以及这是文件还是目录的信息)。或者,您可以直接创建与您的格式对应的 ArchiveEntry 子类。通常,您可能希望在将条目添加到存档之前设置其他标志,例如文件权限或所有者信息。

接下来,您使用 putArchiveEntry 来添加条目,然后开始使用 write 添加条目的内容 - 这里 IOUtils.copy 可能会派上用场。最后,在您编写完所有内容并在添加下一个条目之前调用 closeArchiveEntry。

添加所有条目后,您将调用完成并最终关闭流。

大概架构可能看起来像:

  1. Collection<File> filesToArchive = ...
  2. try (ArchiveOutputStream o = ... create the stream for your format ...) {
  3. for (File f : filesToArchive) {
  4. // maybe skip directories for formats like AR that don't store directories
  5. ArchiveEntry entry = o.createArchiveEntry(f, entryName(f));
  6. // potentially add more flags to entry
  7. o.putArchiveEntry(entry);
  8. if (f.isFile()) {
  9. try (InputStream i = Files.newInputStream(f.toPath())) {
  10. IOUtils.copy(i, o);
  11. }
  12. }
  13. o.closeArchiveEntry();
  14. }
  15. out.finish();
  16. }

如果你想将存档格式与压缩格式结合起来——比如在读取“tar.gz”文件时——你可以将 ArchiveInputStream 包裹在 CompressorInputStream 里面,例如:

  1. try (OutputStream fo = Files.newOutputStream(Paths.get("my.tar.gz"));
  2. OutputStream gzo = new GzipCompressorOutputStream(fo);
  3. ArchiveOutputStream o = new TarArchiveOutputStream(gzo)) {
  4. }

7Z

请注意,Commons Compress 目前仅支持用于 7z 存档的压缩和加密算法的子集。对于仅写入未压缩条目,支持 LZMA、LZMA2、BZIP2 和 Deflate - 除了那些读取支持 AES-256/SHA-256 和 DEFLATE64。

绝对不支持写入多部分存档。可以通过连接部分来读取多部分存档,例如使用 MultiReadOnlySeekableByteChannel。

7z 归档可以使用多种压缩和加密方法以及过滤器组合作为其条目的方法管道。在 Compress 1.8 之前,您在创建档案时只能指定一种方法 - 以前可以使用多种方法读取归档。从 Compress 1.8 开始,可以使用 SevenZOutputFile 的 setContentMethods 方法配置完整的管道。方法是在创建存档时按照它们在管道中出现的顺序指定的,您还可以为某些方法指定某些参数 - 有关详细信息,请参阅 SevenZMethodConfiguration 的 Javadocs。

从存档读取条目时,SevenZArchiveEntry 的 getContentMethods 方法将正确表示压缩/加密/过滤方法,但可能无法确定使用的配置选项。从 Compress 1.8 开始,只能读取用于 LZMA2 的字典大小。

目前,仅在读取档案时支持实体压缩 - 将多个文件压缩为单个块以受益于跨文件重复的模式。这也意味着与原生 7z 可执行文件相比,使用 Commons Compress 时的压缩率可能会更差。

读取或写入需要一个 SeekableByteChannel,它会在读取或写入文件时透明地获得。 org.apache.commons.compress.utils.SeekableInMemoryByteChannel 类允许您读取或写入内存存档。

一些 7z 存档不包含存档条目的任何名称。本机 7zip 工具从存档本身的名称中为此类条目派生一个默认名称。从 Compress 1.19 开始,SevenZFile 有一个模拟这种行为的选项,但默认情况下,未命名的存档条目将从 SevenZArchiveEntry#getName 返回 null。

向 7z 存档添加条目:

  1. SevenZOutputFile sevenZOutput = new SevenZOutputFile(file);
  2. SevenZArchiveEntry entry = sevenZOutput.createArchiveEntry(fileToArchive, name);
  3. sevenZOutput.putArchiveEntry(entry);
  4. sevenZOutput.write(contentOfEntry);
  5. sevenZOutput.closeArchiveEntry();

解压缩给定的 7z 存档(您需要保证会添加异常处理并确保所有流都正确关闭):

  1. SevenZFile sevenZFile = new SevenZFile(new File("archive.7z"));
  2. SevenZArchiveEntry entry = sevenZFile.getNextEntry();
  3. byte[] content = new byte[entry.getSize()];
  4. LOOP UNTIL entry.getSize() HAS BEEN READ {
  5. sevenZFile.read(content, offset, content.length - offset);
  6. }

解压缩给定的内存中 7z 存档:

  1. byte[] inputData; // 7z archive contents
  2. SeekableInMemoryByteChannel inMemoryByteChannel = new SeekableInMemoryByteChannel(inputData);
  3. SevenZFile sevenZFile = new SevenZFile(inMemoryByteChannel);
  4. SevenZArchiveEntry entry = sevenZFile.getNextEntry();
  5. sevenZFile.read(); // read current entry's data

Encrypted 7z Archives

目前 Compress 支持读取但不支持加密档案的写入。 读取加密存档时,必须向 SevenZFile 的构造函数之一提供密码。 如果您尝试在未指定密码的情况下读取加密存档,则会抛出 PasswordRequiredException(IOException 的子类)。

将密码指定为 byte[] 时,一个常见的错误是从 String 创建 byte[] 时使用了错误的编码。 SevenZFile 类期望字节对应于密码的 UTF16-LE 编码。 读取加密存档的示例:

  1. SevenZFile sevenZFile = new SevenZFile(new File("archive.7z"), "secret".getBytes(StandardCharsets.UTF_16LE));
  2. SevenZArchiveEntry entry = sevenZFile.getNextEntry();
  3. byte[] content = new byte[entry.getSize()];
  4. LOOP UNTIL entry.getSize() HAS BEEN READ {
  5. sevenZFile.read(content, offset, content.length - offset);
  6. }

从 Compress 1.17 开始,添加了接受密码为 char[] 而不是 byte[] 的新构造函数。 我们建议您使用这些以避免上述问题。

  1. SevenZFile sevenZFile = new SevenZFile(new File("archive.7z"), "secret".toCharArray());
  2. SevenZArchiveEntry entry = sevenZFile.getNextEntry();
  3. byte[] content = new byte[entry.getSize()];
  4. LOOP UNTIL entry.getSize() HAS BEEN READ {
  5. sevenZFile.read(content, offset, content.length - offset);
  6. }

Random-Access to 7z Archives(随机访问 7z 归档)

在 Compress 1.20 之前,7z 档案只能按顺序读取。 Compress 1.20 引入的 getInputStream(SevenZArchiveEntry)方法现在提供随机访问,但是当存档使用可靠压缩时,随机访问可能比顺序访问慢得多。

Recovering from Certain Broken 7z Archives(从某些损坏的 7z 归档中恢复)

从 Compress 1.19 SevenZFile 开始,SevenZFile 尝试恢复看起来好像它们是第一个卷已被过早删除的多卷存档的一部分的存档。

从 Compress 1.21 开始,此选项必须在 SevenZFileOptions 中显式启用。 恢复工作的方式是通过 Compress 从末尾扫描存档以查找可能看起来像有效的 7z 元数据的内容并使用它,如果它可以成功解析数据块。 这样做时,Compress 可能会遇到看起来像非常大档案的元数据的元数据块,这反过来可能会使 Compress 分配大量内存。 因此,如果您启用恢复,我们强烈建议您在 SevenZFileOptions 中设置内存限制。

ar


除了ArchiveEntry中存储的信息外,ArArchiveEntry还存储有关所有者、用户和组以及Unix权限的信息。

将条目添加到ar存档:

  1. ArArchiveEntry entry = new ArArchiveEntry(name, size);
  2. arOutput.putArchiveEntry(entry);
  3. arOutput.write(contentOfEntry);
  4. arOutput.closeArchiveEntry();

从 ar 归档中读取条目:

  1. ArArchiveEntry entry = (ArArchiveEntry) arInput.getNextEntry();
  2. byte[] content = new byte[entry.getSize()];
  3. LOOP UNTIL entry.getSize() HAS BEEN READ {
  4. arInput.read(content, offset, content.length - offset);
  5. }

传统上,AR 格式不允许文件名超过 16 个字符。 有两种变体以不同的方式规避此限制,即 GNU/SRV4 和 BSD 变体。 Commons Compress 1.0 到 1.2 只能读取使用 GNU/SRV4 变体的档案,Commons Compress 1.3 中添加了对 BSD 变体的支持。 Commons Compress 1.3 还可选地支持使用 BSD 方言编写文件名超过 16 个字符的档案,不支持编写 SVR4/GNU 方言。

无法以可靠的方式检测 AR 存档的结尾,因此 ArArchiveInputStream 将一直读取,直到到达流的末尾或无法将流的内容解析为 AR 条目。

arj

请注意,Commons Compress 尚不支持压缩、加密或多卷 ARJ 存档。

解压缩给定的 arj 存档(您肯定会添加异常处理并确保所有流都正确关闭):

  1. ArjArchiveEntry entry = arjInput.getNextEntry();
  2. byte[] content = new byte[entry.getSize()];
  3. LOOP UNTIL entry.getSize() HAS BEEN READ {
  4. arjInput.read(content, offset, content.length - offset);
  5. }

cpio

除了存储在 ArchiveEntry 中的信息之外,CpioArchiveEntry 还存储各种属性,包括有关原始所有者和权限的信息。

cpio 包以二进制、ASCII 和“带 CRC”变体的形式支持“新可移植”和“旧”格式的 CPIO 存档。

向 cpio 存档添加条目:

  1. CpioArchiveEntry entry = new CpioArchiveEntry(name, size);
  2. cpioOutput.putArchiveEntry(entry);
  3. cpioOutput.write(contentOfEntry);
  4. cpioOutput.closeArchiveEntry();

向 cpio 存档读取条目:

  1. CpioArchiveEntry entry = cpioInput.getNextCPIOEntry();
  2. byte[] content = new byte[entry.getSize()];
  3. LOOP UNTIL entry.getSize() HAS BEEN READ {
  4. cpioInput.read(content, offset, content.length - offset);
  5. }

传统上,CPIO 归档以 512 字节的块写入 - 块大小是 Cpio*Stream 构造函数的配置参数。 从 1.5 版开始,CpioArchiveInputStream 将在到达存档末尾时消耗写入的填充以填充当前块。 不幸的是,许多 CPIO 实现使用更大的块大小,因此在完全使用存档后,原始输入流中可能会留下更多的零字节填充。

jar

通常,JAR 归档是 ZIP 文件,因此 JAR 包支持 ZIP 包提供的所有选项。

为了可互操作 JAR 归档文件应始终使用文件名的 UTF-8 编码(这是默认值)创建。

使用 JarArchiveOutputStream 创建的归档将隐式添加一个 JarMarker 额外字段到归档的第一个档案条目,这将使 Solaris 将它们识别为 Java 归档并允许它们用作可执行文件。

请注意,ArchiveStreamFactory 不区分 ZIP 存档和 JAR 存档,因此如果您对 JAR 存档使用单参数 createArchiveInputStream 方法,它仍将返回更通用的 ZipArchiveInputStream。

JarArchiveEntry 类包含计划在未来支持但从 Compress 1.0 开始不受支持的证书和属性的字段。

向 jar 存档添加条目:

  1. JarArchiveEntry entry = new JarArchiveEntry(name, size);
  2. entry.setSize(size);
  3. jarOutput.putArchiveEntry(entry);
  4. jarOutput.write(contentOfEntry);
  5. jarOutput.closeArchiveEntry();

向 jar 存档读取条目:

  1. JarArchiveEntry entry = jarInput.getNextJarEntry();
  2. byte[] content = new byte[entry.getSize()];
  3. LOOP UNTIL entry.getSize() HAS BEEN READ {
  4. jarInput.read(content, offset, content.length - offset);
  5. }

dump

除了存储在 ArchiveEntry 中的信息之外,DumpArchiveEntry 还存储各种属性,包括原始所有者和权限的信息。

从 Commons Compress 1.3 开始,仅支持使用 new-fs 格式(这是最常见的变体)的转储归档。 现在这个库支持未压缩和 ZLIB 压缩的档案,不能写入归档

从转储归档中读取条目:

  1. DumpArchiveEntry entry = dumpInput.getNextDumpEntry();
  2. byte[] content = new byte[entry.getSize()];
  3. LOOP UNTIL entry.getSize() HAS BEEN READ {
  4. dumpInput.read(content, offset, content.length - offset);
  5. }

在 1.5 版之前,DumpArchiveInputStream 将在读取最后一条记录后关闭原始输入。 从 1.5 版开始,它不会隐式关闭流。

tar

TAR 包有一个专门的文档页面

向 tar 存档添加条目:

  1. TarArchiveEntry entry = new TarArchiveEntry(name);
  2. entry.setSize(size);
  3. tarOutput.putArchiveEntry(entry);
  4. tarOutput.write(contentOfEntry);
  5. tarOutput.closeArchiveEntry();

向 tar 存档读取条目:

  1. TarArchiveEntry entry = tarInput.getNextTarEntry();
  2. byte[] content = new byte[entry.getSize()];
  3. LOOP UNTIL entry.getSize() HAS BEEN READ {
  4. tarInput.read(content, offset, content.length - offset);
  5. }

zip

ZIP 包有一个专门的文档页面

向 zip 存档添加条目:

  1. ZipArchiveEntry entry = new ZipArchiveEntry(name);
  2. entry.setSize(size);
  3. zipOutput.putArchiveEntry(entry);
  4. zipOutput.write(contentOfEntry);
  5. zipOutput.closeArchiveEntry();

如果 ZipArchiveOutputStream 知道它正在写入可搜索的输出而不是不可搜索的流,它可以使用一些利用 SeekableByteChannel 的内部优化。 如果要写入文件,则应使用接受 File 或 SeekableByteChannel 参数的构造函数,而不是使用 OutputStream 或 ArchiveStreamFactory 中的工厂方法的构造函数。

从 zip 存档中读取条目:

  1. ZipArchiveEntry entry = zipInput.getNextZipEntry();
  2. byte[] content = new byte[entry.getSize()];
  3. LOOP UNTIL entry.getSize() HAS BEEN READ {
  4. zipInput.read(content, offset, content.length - offset);
  5. }

使用推荐的 ZipFile 类从 zip 存档中读取条目:

  1. ZipArchiveEntry entry = zipFile.getEntry(name);
  2. InputStream content = zipFile.getInputStream(entry);
  3. try {
  4. READ UNTIL content IS EXHAUSTED
  5. } finally {
  6. content.close();
  7. }

使用 SeekableInMemoryByteChannel 和 ZipFile 类从内存中的 zip 存档中读取条目:

  1. byte[] inputData; // zip archive contents
  2. SeekableInMemoryByteChannel inMemoryByteChannel = new SeekableInMemoryByteChannel(inputData);
  3. ZipFile zipFile = new ZipFile(inMemoryByteChannel);
  4. ZipArchiveEntry archiveEntry = zipFile.getEntry("entryName");
  5. InputStream inputStream = zipFile.getInputStream(archiveEntry);
  6. inputStream.read() // read data from the input stream

使用多线程创建 zip 文件:

创建 zip 文件的简单实现可能如下所示:

  1. public class ScatterSample {
  2. ParallelScatterZipCreator scatterZipCreator = new ParallelScatterZipCreator();
  3. ScatterZipOutputStream dirs = ScatterZipOutputStream.fileBased(File.createTempFile("scatter-dirs", "tmp"));
  4. public ScatterSample() throws IOException {
  5. }
  6. public void addEntry(ZipArchiveEntry zipArchiveEntry, InputStreamSupplier streamSupplier) throws IOException {
  7. if (zipArchiveEntry.isDirectory() && !zipArchiveEntry.isUnixSymlink())
  8. dirs.addArchiveEntry(ZipArchiveEntryRequest.createZipArchiveEntryRequest(zipArchiveEntry, streamSupplier));
  9. else
  10. scatterZipCreator.addArchiveEntry( zipArchiveEntry, streamSupplier);
  11. }
  12. public void writeTo(ZipArchiveOutputStream zipArchiveOutputStream)
  13. throws IOException, ExecutionException, InterruptedException {
  14. dirs.writeTo(zipArchiveOutputStream);
  15. dirs.close();
  16. scatterZipCreator.writeTo(zipArchiveOutputStream);
  17. }
  18. }

Compressors

Concatenated Streams(连接流)

对于 bzip2、gzip 和 xz 格式以及带框架的 lz4 格式,单个压缩文件实际上可能包含多个流,这些流将在解压缩时由命令行实用程序连接起来。 从 Commons Compress 1.4 开始,这些格式的 *CompressorInputStreams 也支持连接流,但默认情况下它们不会这样做。 您必须使用双参数构造函数并显式启用支持。

Brotli

这个包的实现由 Google Brotli dec 库提供。

解压缩给定的 Brotli 压缩文件(您肯定会添加异常处理并确保所有流都正确关闭):

  1. InputStream fin = Files.newInputStream(Paths.get("archive.tar.br"));
  2. BufferedInputStream in = new BufferedInputStream(fin);
  3. OutputStream out = Files.newOutputStream(Paths.get("archive.tar"));
  4. BrotliCompressorInputStream brIn = new BrotliCompressorInputStream(in);
  5. final byte[] buffer = new byte[buffersize];
  6. int n = 0;
  7. while (-1 != (n = brIn.read(buffer))) {
  8. out.write(buffer, 0, n);
  9. }
  10. out.close();
  11. brIn.close();

bzip2

请注意,BZipCompressorOutputStream 在内存中保留了一些大数据结构。 在不再需要任何流时应该立即关闭它,但这对于 BZipCompressorOutputStream 更为重要。

解压缩给定的 bzip2 压缩文件(您肯定会添加异常处理并确保所有流都正确关闭):

  1. InputStream fin = Files.newInputStream(Paths.get("archive.tar.bz2"));
  2. BufferedInputStream in = new BufferedInputStream(fin);
  3. OutputStream out = Files.newOutputStream(Paths.get("archive.tar"));
  4. BZip2CompressorInputStream bzIn = new BZip2CompressorInputStream(in);
  5. final byte[] buffer = new byte[buffersize];
  6. int n = 0;
  7. while (-1 != (n = bzIn.read(buffer))) {
  8. out.write(buffer, 0, n);
  9. }
  10. out.close();
  11. bzIn.close();

使用 bzip2 压缩给定文件(您肯定会添加异常处理并确保所有流都正确关闭):

  1. InputStream in = Files.newInputStream(Paths.get("archive.tar"));
  2. OutputStream fout = Files.newOutputStream(Paths.get("archive.tar.bz2"));
  3. BufferedOutputStream out = new BufferedOutputStream(fout);
  4. BZip2CompressorOutputStream bzOut = new BZip2CompressorOutputStream(out);
  5. final byte[] buffer = new byte[buffersize];
  6. int n = 0;
  7. while (-1 != (n = in.read(buffer))) {
  8. bzOut.write(buffer, 0, n);
  9. }
  10. bzOut.close();
  11. in.close();

DEFLATE

该包使用的 DEFLATE/INFLATE 代码的实现由 Java 类库的 java.util.zip 包提供。

解压缩给定的 DEFLATE 压缩文件(您肯定会添加异常处理并确保所有流都正确关闭):

  1. InputStream fin = Files.newInputStream(Paths.get("some-file"));
  2. BufferedInputStream in = new BufferedInputStream(fin);
  3. OutputStream out = Files.newOutputStream(Paths.get("archive.tar"));
  4. DeflateCompressorInputStream defIn = new DeflateCompressorInputStream(in);
  5. final byte[] buffer = new byte[buffersize];
  6. int n = 0;
  7. while (-1 != (n = defIn.read(buffer))) {
  8. out.write(buffer, 0, n);
  9. }
  10. out.close();
  11. defIn.close();

使用 DEFLATE 压缩给定文件(您肯定会添加异常处理并确保所有流都正确关闭):

  1. InputStream in = Files.newInputStream(Paths.get("archive.tar"));
  2. OutputStream fout = Files.newOutputStream(Paths.get("some-file"));
  3. BufferedOutputStream out = new BufferedOutputStream(fout);
  4. DeflateCompressorOutputStream defOut = new DeflateCompressorOutputStream(out);
  5. final byte[] buffer = new byte[buffersize];
  6. int n = 0;
  7. while (-1 != (n = in.read(buffer))) {
  8. defOut.write(buffer, 0, n);
  9. }
  10. defOut.close();
  11. in.close();

DEFLATE64

解压缩给定的 DEFLATE64 压缩文件(您肯定会添加异常处理并确保所有流都正确关闭):

  1. InputStream fin = Files.newInputStream(Paths.get("some-file"));
  2. BufferedInputStream in = new BufferedInputStream(fin);
  3. OutputStream out = Files.newOutputStream(Paths.get("archive.tar"));
  4. Deflate64CompressorInputStream defIn = new Deflate64CompressorInputStream(in);
  5. final byte[] buffer = new byte[buffersize];
  6. int n = 0;
  7. while (-1 != (n = defIn.read(buffer))) {
  8. out.write(buffer, 0, n);
  9. }
  10. out.close();
  11. defIn.close();

gzip

该包使用的 DEFLATE/INFLATE 代码的实现由 Java 类库的 java.util.zip 包提供。

解压缩给定的 gzip 压缩文件(您肯定会添加异常处理并确保所有流都正确关闭):

  1. InputStream fin = Files.newInputStream(Paths.get("archive.tar.gz"));
  2. BufferedInputStream in = new BufferedInputStream(fin);
  3. OutputStream out = Files.newOutputStream(Paths.get("archive.tar"));
  4. GzipCompressorInputStream gzIn = new GzipCompressorInputStream(in);
  5. final byte[] buffer = new byte[buffersize];
  6. int n = 0;
  7. while (-1 != (n = gzIn.read(buffer))) {
  8. out.write(buffer, 0, n);
  9. }
  10. out.close();
  11. gzIn.close();

使用 gzip 压缩给定文件(您肯定会添加异常处理并确保正确关闭所有流):

  1. InputStream in = Files.newInputStream(Paths.get("archive.tar"));
  2. OutputStream fout = Files.newOutputStream(Paths.get("archive.tar.gz"));
  3. BufferedOutputStream out = new BufferedOutputStream(fout);
  4. GzipCompressorOutputStream gzOut = new GzipCompressorOutputStream(out);
  5. final byte[] buffer = new byte[buffersize];
  6. int n = 0;
  7. while (-1 != (n = in.read(buffer))) {
  8. gzOut.write(buffer, 0, n);
  9. }
  10. gzOut.close();
  11. in.close();

LZ4

lz4 有两种不同的“格式”。 称为“块格式”的格式仅包含原始压缩数据,而另一种提供更高级别的“帧格式” - Commons Compress 提供两种不同的流类来读取或写入任何一种格式。

解压缩给定的框架 LZ4 文件(您肯定会添加异常处理并确保所有流都正确关闭):

  1. InputStream fin = Files.newInputStream(Paths.get("archive.tar.lz4"));
  2. BufferedInputStream in = new BufferedInputStream(fin);
  3. OutputStream out = Files.newOutputStream(Paths.get("archive.tar"));
  4. FramedLZ4CompressorInputStream zIn = new FramedLZ4CompressorInputStream(in);
  5. final byte[] buffer = new byte[buffersize];
  6. int n = 0;
  7. while (-1 != (n = zIn.read(buffer))) {
  8. out.write(buffer, 0, n);
  9. }
  10. out.close();
  11. zIn.close();

使用 LZ4 帧格式压缩给定文件(您肯定会添加异常处理并确保正确关闭所有流):

  1. InputStream in = Files.newInputStream(Paths.get("archive.tar"));
  2. OutputStream fout = Files.newOutputStream(Paths.get("archive.tar.lz4"));
  3. BufferedOutputStream out = new BufferedOutputStream(fout);
  4. FramedLZ4CompressorOutputStream lzOut = new FramedLZ4CompressorOutputStream(out);
  5. final byte[] buffer = new byte[buffersize];
  6. int n = 0;
  7. while (-1 != (n = in.read(buffer))) {
  8. lzOut.write(buffer, 0, n);
  9. }
  10. lzOut.close();
  11. in.close();

lzma

该包的实现由公共领域 XZ for Java 库提供。

解压缩给定的 lzma 压缩文件(您肯定会添加异常处理并确保所有流都正确关闭):

  1. InputStream fin = Files.newInputStream(Paths.get("archive.tar.lzma"));
  2. BufferedInputStream in = new BufferedInputStream(fin);
  3. OutputStream out = Files.newOutputStream(Paths.get("archive.tar"));
  4. LZMACompressorInputStream lzmaIn = new LZMACompressorInputStream(in);
  5. final byte[] buffer = new byte[buffersize];
  6. int n = 0;
  7. while (-1 != (n = xzIn.read(buffer))) {
  8. out.write(buffer, 0, n);
  9. }
  10. out.close();
  11. lzmaIn.close();

使用 lzma 压缩给定文件(您肯定会添加异常处理并确保所有流都正确关闭):

  1. InputStream in = Files.newInputStream(Paths.get("archive.tar"));
  2. OutputStream fout = Files.newOutputStream(Paths.get("archive.tar.lzma"));
  3. BufferedOutputStream out = new BufferedOutputStream(fout);
  4. LZMACompressorOutputStream lzOut = new LZMACompressorOutputStream(out);
  5. final byte[] buffer = new byte[buffersize];
  6. int n = 0;
  7. while (-1 != (n = in.read(buffer))) {
  8. lzOut.write(buffer, 0, n);
  9. }
  10. lzOut.close();
  11. in.close();

Pack200

Pack200 软件包有一个专门的文档页面

该包的实现以前由 Java 类库的 java.util.zip 包提供。 从 Compress 1.21 开始,该实现使用 Compress 本身附带的现已退役的 Apache Harmony™ 项目的 pack200 代码的副本。

解压缩给定的 pack200 压缩文件(您肯定会添加异常处理并确保所有流都正确关闭):

  1. InputStream fin = Files.newInputStream(Paths.get("archive.pack"));
  2. BufferedInputStream in = new BufferedInputStream(fin);
  3. OutputStream out = Files.newOutputStream(Paths.get("archive.jar"));
  4. Pack200CompressorInputStream pIn = new Pack200CompressorInputStream(in);
  5. final byte[] buffer = new byte[buffersize];
  6. int n = 0;
  7. while (-1 != (n = pIn.read(buffer))) {
  8. out.write(buffer, 0, n);
  9. }
  10. out.close();
  11. pIn.close();

使用 pack200 压缩给定文件(您肯定会添加异常处理并确保所有流都正确关闭):

  1. InputStream in = Files.newInputStream(Paths.get("archive.jar"));
  2. OutputStream fout = Files.newOutputStream(Paths.get("archive.pack"));
  3. BufferedOutputStream out = new BufferedInputStream(fout);
  4. Pack200CompressorOutputStream pOut = new Pack200CompressorOutputStream(out);
  5. final byte[] buffer = new byte[buffersize];
  6. int n = 0;
  7. while (-1 != (n = in.read(buffer))) {
  8. pOut.write(buffer, 0, n);
  9. }
  10. pOut.close();
  11. in.close();

Snappy

Snappy 使用了两种不同的“格式”,一种只包含原始压缩数据,而另一种提供更高级别的“帧格式”——Commons Compress 提供两种不同的流类来读取任何一种格式。

从 1.12 开始,我们添加了对可以在构造流时指定的帧格式的不同方言的支持。 STANDARD 方言遵循“框架格式”规范,而 IWORK_ARCHIVE 方言可用于解析属于 Apple iWork 13 格式的 IWA 文件。 如果没有指定方言,则使用 STANDARD。 CompressorStreamFactory 只能检测到 STANDARD 格式。

解压缩给定的框架 Snappy 文件(您肯定会添加异常处理并确保所有流都正确关闭):

  1. InputStream fin = Files.newInputStream(Paths.get("archive.tar.sz"));
  2. BufferedInputStream in = new BufferedInputStream(fin);
  3. OutputStream out = Files.newOutputStream(Paths.get("archive.tar"));
  4. FramedSnappyCompressorInputStream zIn = new FramedSnappyCompressorInputStream(in);
  5. final byte[] buffer = new byte[buffersize];
  6. int n = 0;
  7. while (-1 != (n = zIn.read(buffer))) {
  8. out.write(buffer, 0, n);
  9. }
  10. out.close();
  11. zIn.close();

使用框架 Snappy 压缩给定文件(您肯定会添加异常处理并确保所有流都正确关闭):

  1. InputStream in = Files.newInputStream(Paths.get("archive.tar"));
  2. OutputStream fout = Files.newOutputStream(Paths.get("archive.tar.sz"));
  3. BufferedOutputStream out = new BufferedOutputStream(fout);
  4. FramedSnappyCompressorOutputStream snOut = new FramedSnappyCompressorOutputStream(out);
  5. final byte[] buffer = new byte[buffersize];
  6. int n = 0;
  7. while (-1 != (n = in.read(buffer))) {
  8. snOut.write(buffer, 0, n);
  9. }
  10. snOut.close();
  11. in.close();

XZ

该包的实现由公共领域 XZ for Java 库提供。

当您尝试使用 CompressorStreamFactory 打开 XZ 流进行读取时,Commons Compress 将检查 XZ for Java 库是否可用。 从 Compress 1.9 开始,此检查的结果将被缓存,除非 Compress 在其类路径中找到 OSGi 类。 您可以使用 XZUtils#setCacheXZAvailability 覆盖此默认行为。

解压缩给定的 XZ 压缩文件(您肯定会添加异常处理并确保所有流都正确关闭):

  1. InputStream fin = Files.newInputStream(Paths.get("archive.tar.xz"));
  2. BufferedInputStream in = new BufferedInputStream(fin);
  3. OutputStream out = Files.newOutputStream(Paths.get("archive.tar"));
  4. XZCompressorInputStream xzIn = new XZCompressorInputStream(in);
  5. final byte[] buffer = new byte[buffersize];
  6. int n = 0;
  7. while (-1 != (n = xzIn.read(buffer))) {
  8. out.write(buffer, 0, n);
  9. }
  10. out.close();
  11. xzIn.close();

使用 XZ 压缩给定文件(您肯定会添加异常处理并确保所有流都正确关闭):

  1. InputStream in = Files.newInputStream(Paths.get("archive.tar"));
  2. OutputStream fout = Files.newOutputStream(Paths.get("archive.tar.xz"));
  3. BufferedOutputStream out = new BufferedInputStream(fout);
  4. XZCompressorOutputStream xzOut = new XZCompressorOutputStream(out);
  5. final byte[] buffer = new byte[buffersize];
  6. int n = 0;
  7. while (-1 != (n = in.read(buffer))) {
  8. xzOut.write(buffer, 0, n);
  9. }
  10. xzOut.close();
  11. in.close();

Z

解压缩给定的 Z 压缩文件(您肯定会添加异常处理并确保所有流都正确关闭):

  1. InputStream fin = Files.newInputStream(Paths.get("archive.tar.Z"));
  2. BufferedInputStream in = new BufferedInputStream(fin);
  3. OutputStream out = Files.newOutputStream(Paths.get("archive.tar"));
  4. ZCompressorInputStream zIn = new ZCompressorInputStream(in);
  5. final byte[] buffer = new byte[buffersize];
  6. int n = 0;
  7. while (-1 != (n = zIn.read(buffer))) {
  8. out.write(buffer, 0, n);
  9. }
  10. out.close();
  11. zIn.close();

Zstandard

这个包的实现由 Zstandard JNI 库提供。

解压缩给定的 Zstandard 压缩文件(您肯定会添加异常处理并确保正确关闭所有流):

  1. InputStream fin = Files.newInputStream(Paths.get("archive.tar.zstd"));
  2. BufferedInputStream in = new BufferedInputStream(fin);
  3. OutputStream out = Files.newOutputStream(Paths.get("archive.tar"));
  4. ZstdCompressorInputStream zsIn = new ZstdCompressorInputStream(in);
  5. final byte[] buffer = new byte[buffersize];
  6. int n = 0;
  7. while (-1 != (n = zsIn.read(buffer))) {
  8. out.write(buffer, 0, n);
  9. }
  10. out.close();
  11. zsIn.close();

使用 Zstandard 压缩给定文件(您肯定会添加异常处理并确保所有流都正确关闭):

  1. InputStream in = Files.newInputStream(Paths.get("archive.tar"));
  2. OutputStream fout = Files.newOutputStream(Paths.get("archive.tar.zstd"));
  3. BufferedOutputStream out = new BufferedOutputStream(fout);
  4. ZstdCompressorOutputStream zOut = new ZstdCompressorOutputStream(out);
  5. final byte[] buffer = new byte[buffersize];
  6. int n = 0;
  7. while (-1 != (n = in.read(buffer))) {
  8. zOut.write(buffer, 0, n);
  9. }
  10. zOut.close();
  11. in.close();

Extending Commons Compress

从 1.13 版开始,现在可以使用 Java 的 ServiceLoader 机制添加 Compressor- 和 ArchiverStream 实现。

Extending Commons Compress Compressors


要提供您自己的压缩器,您必须在类路径上提供一个名为 META-INF/services/org.apache.commons.compress.compressors.CompressorStreamProvider 的文件。

该文件每行必须包含一个完全限定的类名。

例如:

  1. org.apache.commons.compress.compressors.TestCompressorStreamProvider

此类必须实现 Commons Compress 接口 org.apache.commons.compress.compressors.CompressorStreamProvider

Extending Commons Compress Archivers

要提供您自己的压缩器,您必须在类路径中提供一个名为META-INF/services/org.apache.commons.compress.archivers.ArchiveStreamProvider 的文件。

该文件每行必须包含一个完全限定的类名。

例如:

  1. org.apache.commons.compress.archivers.TestArchiveStreamProvider

此类必须实现 Commons Compress 接口 org.apache.commons.compress.archivers.ArchiveStreamProvider