我有一个GZIPInputStream
,我是从另一个ByteArrayInputStream
构建的。我想知道gzip数据的原始(未压缩)长度。虽然我可以读到GZIPInputStream
的末尾,然后计算数字,但它会花费很多时间并浪费CPU。我想在阅读之前知道尺寸。
ZipEntry.getSize()
是否有类似GZIPInputStream
的类似方法:
public long getSize ()
自: API级别1 获取此ZipEntry的未压缩大小。
答案 0 :(得分: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时,无法确定正确的未压缩大小!
答案 1 :(得分:4)
根据@ 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();
答案 2 :(得分:2)
答案 3 :(得分:2)
除了解压缩整个事物之外,没有可靠的方法来获得长度。请参阅Uncompressed file size using zlib's gzip file access function。
答案 4 :(得分: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相当于@Alex的答案]
答案 5 :(得分:0)
不,不幸的是,如果您想获得未压缩的大小,您必须阅读整个流并按照您在问题中提到的方式增加一个计数器。为什么你需要知道尺寸?可以根据您的目的估算尺寸吗?
答案 6 :(得分:0)
基于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;
}
答案 7 :(得分: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);
}
}
}
}