Apache Commons Compress 中文文档

何承
2023-12-01

原文地址

一般的笔记

文档和压缩机

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支持的许多压缩和加密算法的档案,但不支持在写档案时加密。

缓冲

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

工厂

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

使用算法名创建一个压缩程序写入给定的输出:

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

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

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

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

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

注意,无法检测lzma或Brotli格式,因此只能使用双参数版本的createCompressorInputStream。在压缩1.9之前,也没有自动检测到. z格式。

限制内存使用

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

因为Compress 1.19, SevenZFile 也有一个可选的构造函数来传递内存上限。支持LZMA压缩流。

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

统计数据

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

Archivers

不支持的功能

许多受支持的格式开发了不同的方言和扩展,有些格式允许Commons Compress支持的特性(还没有)。

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

条目名称

所有归档格式都通过 ArchiveEntry 的实例(或者更确切地说,它的子类)提供关于单个归档条目的元数据。从归档中读取时,getName方法提供的信息是存储在归档中的原始名称。不能保证该名称表示一个相对文件名,甚至是目标操作系统上的一个有效文件名。当尝试从条目名称创建文件名时,应该再次检查结果。

常见的提取逻辑

除了7z之外,所有格式都提供了 ArchiveInputStream 的一个子类,可以用来创建归档。对于7z, SevenZFile 提供了一个类似的API,它不表示流,因为我们的实现要求对输入进行随机访问,不能用于一般的流。ZIP实现也可以从随机访问中获益很多,详细信息请参阅ZIP页面。

假设您想要将归档提取到一个目标目录(您将调用 getNextEntry),验证可以读取条目,从条目的名称构造一个健全的文件名,创建一个 File 并将所有内容写入其中——这里是 IOUtils.copy 可能会派上用场。在 getNextEntry 返回 null 之前,您可以对每个条目执行此操作。

骨骼可能看起来像:

File targetDir = ...
try (ArchiveInputStream i = ... create the stream for your format, use buffering...) {
    ArchiveEntry entry = null;
    while ((entry = i.getNextEntry()) != null) {
        if (!i.canReadEntryData(entry)) {
            // log something?
            continue;
        }
        String name = fileName(targetDir, entry);
        File f = new File(name);
        if (entry.isDirectory()) {
            if (!f.isDirectory() && !f.mkdirs()) {
                throw new IOException("failed to create directory " + f);
            }
        } else {
            File parent = f.getParentFile();
            if (!parent.isDirectory() && !parent.mkdirs()) {
                throw new IOException("failed to create directory " + parent);
            }
            try (OutputStream o = Files.newOutputStream(f.toPath())) {
                IOUtils.copy(i, o);
            }
        }
    }
}

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

如果您想将存档格式与压缩格式结合在一起——比如在读取“tar”时。你把 ArchiveInputStream 包在 CompressorInputStream 周围,例如:

try (InputStream fi = Files.newInputStream(Paths.get("my.tar.gz"));
     InputStream bi = new BufferedInputStream(fi);
     InputStream gzi = new GzipCompressorInputStream(bi);
     ArchiveInputStream o = new TarArchiveInputStream(gzi)) {
}
 类似资料: