11

我有一个GZIPInputStream我从另一个构建的ByteArrayInputStream。我想知道 gzip 数据的原始(未压缩)长度。虽然我可以读到最后GZIPInputStream,然后数数,但会耗费大量时间并浪费 CPU。我想在阅读之前知道大小。

有没有类似ZipEntry.getSize()for的方法GZIPInputStream

public long getSize ()
自: API 级别 1
获取此 ZipEntry 的未压缩大小。

4

8 回答 8

8

可以通过读取 gzip 文件的最后四个字节来确定未压缩的大小。

我在这里找到了这个解决方案:

http://www.abeel.be/content/determine-uncompressed-size-gzip-file

同样来自这个链接有一些示例代码(更正为使用long而不是int,以应对 2GB 和 4GB 之间的大小,这将导致int环绕):

RandomAccessFile raf = new RandomAccessFile(file, "r");
raf.seek(raf.length() - 4);
byte b4 = raf.read();
byte b3 = raf.read();
byte b2 = raf.read();
byte b1 = raf.read();
long val = ((long)b1 << 24) | ((long)b2 << 16) | ((long)b3 << 8) | (long)b4;
raf.close();

val是以字节为单位的长度。注意:当未压缩文件大于 4GB 时,您无法确定正确的未压缩大小!

于 2011-09-23T12:31:11.843 回答
6

基于@Alexander 的回答:

RandomAccessFile raf = new RandomAccessFile(inputFilePath + ".gz", "r");
raf.seek(raf.length() - 4);
byte[] bytes = new byte[4];
raf.read(bytes);
fileSize = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getInt();
if (fileSize < 0)
  fileSize += (1L << 32);
raf.close();
于 2012-10-02T04:31:59.707 回答
2

如果您可以猜测压缩率(如果数据与您已经处理的其他数据相似,这是一个合理的预期),那么您可以计算出任意大文件的大小(有一些错误)。同样,这假定文件包含单个 gzip 流。以下假设大于 90% 的估计尺寸(基于估计的比率)的第一个尺寸是真实尺寸:

estCompRatio = 6.1;
RandomAccessFile raf = new RandomAccessFile(inputFilePath + ".gz", "r");
compLength = raf.length();
byte[] bytes = new byte[4];
raf.read(bytes);
uncLength = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getInt();
raf.seek(compLength - 4);
uncLength = raf.readInt();
while(uncLength < (compLength * estCompRatio * 0.9)){
  uncLength += (1L << 32);
}

[将 estCompRatio 设置为 0 相当于@Alexander 的回答]

于 2012-10-29T04:23:48.603 回答
2

GZIPInputStream 是否有类似 ZipEntry.getSize() 的方法

不,它不在Javadoc中=> 它不存在。

你需要什么长度

于 2011-09-06T09:34:10.730 回答
2

基于 4 个尾字节的计算的更紧凑版本(避免使用字节缓冲区,调用Integer.reverseBytes以反转读取字节的字节顺序)。

private static long getUncompressedSize(Path inputPath) throws IOException
{
    long size = -1;
    try (RandomAccessFile fp = new RandomAccessFile(inputPath.toFile(), "r")) {        
        fp.seek(fp.length() - Integer.BYTES);
        int n = fp.readInt();
        size = Integer.toUnsignedLong(Integer.reverseBytes(n));
    }
    return size;
}
于 2018-04-05T21:45:58.687 回答
1

除了解压缩整个东西之外,没有可靠的方法来获得长度。请参阅使用 zlib 的 gzip 文件访问功能的未压缩文件大小

于 2012-10-02T04:38:48.430 回答
0

而是从底层 FileInputStream 获取 FileChannel。它告诉您压缩文件的文件大小和当前位置。例子:

@Override
public void produce(final DataConsumer consumer, final boolean skipData) throws IOException {
    try (FileInputStream fis = new FileInputStream(tarFile)) {
        FileChannel channel = fis.getChannel();
        final Eta<Long> eta = new Eta<>(channel.size());
        try (InputStream is = tarFile.getName().toLowerCase().endsWith("gz")
            ? new GZIPInputStream(fis) : fis) {
            try (TarArchiveInputStream tais = (TarArchiveInputStream) new ArchiveStreamFactory()
                .createArchiveInputStream("tar", new BufferedInputStream(is))) {

                TarArchiveEntry tae;
                boolean done = false;
                while (!done && (tae = tais.getNextTarEntry()) != null) {
                    if (tae.getName().startsWith("docs/") && tae.getName().endsWith(".html")) {
                        String data = null;
                        if (!skipData) {
                            data = new String(tais.readNBytes((int) tae.getSize()), StandardCharsets.UTF_8);
                        }
                        done = !consumer.consume(data);
                    }

                    String progress = eta.toStringPeriodical(channel.position());
                    if (progress != null) {
                        System.out.println(progress);
                    }
                }
                System.out.println("tar bytes read: " + tais.getBytesRead());
            } catch (ArchiveException ex) {
                throw new IOException(ex);
            }
        }
    }
}
于 2019-02-18T05:24:57.953 回答
-1

不,不幸的是,如果您想获得未压缩的大小,则必须读取整个流并增加一个计数器,就像您在问题中提到的那样。为什么需要知道尺寸?对大小的估计是否适合您的目的?

于 2011-09-06T13:29:38.700 回答