获取 GZIP 文件属性(如 'gzip -l',基本上是压缩率)

Get GZIPped file attributes (like 'gzip -l', basically compression ratio)

我有一个非常大的 gzip 文件树目录,我需要计算未压缩的大小。 由于我所说的不仅仅是 600GB(压缩),我认为解压缩每个文件以验证大小不是正确的方法。

Unix shell,我使用命令 gzip -l 轻松完成了这项任务,列出了文件夹中的每个文件以及压缩率、压缩和未压缩的大小。

虽然,我找到的 Java 个与 GZIP 相关的库只是用于压缩和解压缩的流。

如果 gzip 命令可以在不触及文件的情况下检索此信息,我假设此数据必须在文件的某种 header 上指定。在不解压缩文件的情况下访问此信息的方式是什么?

查看 Apache Commons Compress,它支持 gzip。它还有一个 class 'org.apache.commons.compress.compressors.gzip.GzipParameters' 可能会有帮助。

根据 GZIP 规范 RFC 1952 GZIP 块的最后 4 个字节是数据的未压缩大小。该值存储在小端。大多数 gzip 文件只有 1 个块,因此这将是文件的最后 4 个字节。

例如,我刚刚压缩了一个未压缩大小为 29963246 字节的文件。 gzip 文件中的最后 4 个字节是

EE 33 C9 01

当读取小端时(从右到左)0x1C933EE = 29963246

这是一种通过仅读取小尾数法的最后 4 个字节来获取未压缩文件大小的快速而肮脏的方法:

File f = ...
try(RandomAccessFile ra =new RandomAccessFile(f, "r");
    FileChannel channel = ra.getChannel()){

        MappedByteBuffer fileBuffer = channel.map(MapMode.READ_ONLY, f.length()-4, 4);
        fileBuffer.load();
        
        ByteBuffer buf = ByteBuffer.allocate(4);
        buf.order(ByteOrder.LITTLE_ENDIAN);
        
        
        buf.put(fileBuffer);
        buf.flip();
        //will print the uncompressed size
        //getInt() reads the 4 bytes as a int
        // if the file is between 2GB and 4GB
        // then this will return a negative value
        //and you'll have to do your own converting to an unsigned int
        System.out.println(buf.getInt());
    }

编辑

请注意,这仅适用于只有 1 个压缩块的 gzip 文件(大多数文件 < 4GB)。如果你有一个包含多个 gzip 块的文件,这将只有最后一个块的大小 return。由于规范只分配了 4 个字节的大小,我假设一个大于 4GB 的文件将被分成多个 GZIP 块。

一个更健壮的版本是解析每个 gzip 块以获得每个块的未压缩大小。 GZIP header 还具有压缩数据的大小,因此您必须解析每个 GZIP 块 header,获取压缩数据的长度,寻找该长度以获得 GZIP 块的末尾,然后得到未压缩的大小来总结。然后继续解析任何其他 GZIP 块,直到到达 EOF。