获取 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。
我有一个非常大的 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。