java zip 流压缩_关于zip:检测流是否用Java压缩的最佳方法

这篇博客探讨了如何确定Java的InputStream是否包含ZIP压缩数据。建议通过尝试使用ZipInputStream读取流来检测,如果getNextEntry()返回非空值,则表明流是被压缩的。此外,还提供了代码示例展示了解压缩ZIP流的方法,以及关于在处理流时的注意事项,如ZIP文件的魔术字节检测和兼容性问题。
摘要由CSDN通过智能技术生成

找出i java.io.InputStream包含压缩数据的最佳方法是什么?

这是HTTP请求/响应的一部分吗?

介绍

由于所有答案都存在5年之久,因此我有责任写下来,这是怎么回事。我严重怀疑应该读取流的魔术字节!那是一个低级代码,通常应该避免。

简单的答案

miku写道:

If the Stream can be read via ZipInputStream, it should be zipped.

是的,但是在ZipInputStream的情况下,"可以读取"表示第一次调用.getNextEntry()会返回非空值。没有例外,等等。因此,您可以执行魔术字节解析,而无需执行以下操作:

boolean isZipped = new ZipInputStream(yourInputStream).getNextEntry() != null;

就是这样!

一般解压缩的想法

通常,似乎在[解压缩]时处理文件比使用流更加方便。有几个有用的库,而且ZipFile比ZipInputStream具有更多的功能。此处讨论zip文件的处理:什么是zip / unzip文件的良好Java库?因此,如果您可以使用文件,则更好!

代码样例

我需要在应用程序中仅使用流。这就是我写的用于解压缩的方法:

import org.apache.commons.io.IOUtils;

import java.util.zip.ZipEntry;

import java.util.zip.ZipInputStream;

public boolean unzip(InputStream inputStream, File outputFolder) throws IOException {

ZipInputStream zis = new ZipInputStream(inputStream);

ZipEntry entry;

boolean isEmpty = true;

while ((entry = zis.getNextEntry()) != null) {

isEmpty = false;

File newFile = new File(outputFolder, entry.getName());

if (newFile.getParentFile().mkdirs() && !entry.isDirectory()) {

FileOutputStream fos = new FileOutputStream(newFile);

IOUtils.copy(zis, fos);

IOUtils.closeQuietly(fos);

}

}

IOUtils.closeQuietly(zis);

return !isEmpty;

}

在某些情况下,ZipOutputStream未完成或未正确关闭,这意味着在将其解析为new ZipFile(f)时,结果文件将抛出IOException,因为该文件无效。即使zip文件因其他目的无效,上述操作也不会失败。

zis.getNextEntry()使InputStream前进。如果您想从InputStream重新开始,以防它不是一个zip文件,则不能这样做,因为zis.getNextEntry()使InputStream前进了。

@Luke Hm,您也许正确,您测试了吗?我是在很久以前写的,所以我不

是。我找到了一个解决方案:在将inputStream传递给ZipInputStream之前,将其包装为BufferedInputStream,这样就可以在其上调用mark()和reset()了。 stackoverflow.com/a/53047891/4265610

ZIP格式的魔术字节为50 4B。您可以测试流(使用标记和重置-您可能需要缓冲),但是我不希望这是100%可靠的方法。无法将其与以字母PK开头的US-ASCII编码文本文件区分开。

最好的方法是在打开流之前在内容格式上提供元数据,然后对其进行适当处理。

您可以检查流的前四个字节是本地文件头签名,该签名启动本地文件头,该本地文件头继续处理ZIP文件中的每个文件,如此处的规范所示为50 4B 03 04。

一点测试代码显示了它的工作原理:

byte[] buffer = new byte[4];

try {

ZipOutputStream zos = new ZipOutputStream(new FileOutputStream("so.zip"));

ZipEntry ze = new ZipEntry("HelloWorld.txt");

zos.putNextEntry(ze);

zos.write("Hello world".getBytes());

zos.close();

FileInputStream is = new FileInputStream("so.zip");

is.read(buffer);

is.close();

}

catch(IOException e) {

e.printStackTrace();

}

for (byte b : buffer) {

System.out.printf("%H",b);

}

给我这个输出:

50 4B 3 4

我也有相同的想法(尽管在规范上值得信赖的Wikipedia-太可惜了!),但是看来这不是一种可靠的机制:"实施者应该意识到,使用或不使用此签名来标记数据描述符,都可能会遇到ZIP文件。读取ZIP文件以确保兼容性时,两种情况都应考虑在内。"

从一般角度来看,这是正确的,但我的猜测是,如果您没有签名ZipInputStream,它将因为坚持ZipEntry对象而失败。

您可以在zip文件(例如Microsoft Windows可执行文件)之前添加随机垃圾。仅当您使用中央目录而不是使用本地标头流式传输时,它们才起作用。 FWIW,Java PlugIn和WebStart使用中央目录,但现在也检查前四个字节(请参阅GIAR)。

(抱歉,GIFAR。)

不知道ZipInputStream是否会在该输入上失败。在一个智能的实现中,它将向前寻找并找到该签名。这是在自解压归档文件中完成的方式,在Windows上,无论zip条目位于何处,在文件的开头都具有PE-COFF签名,并且在文件内具有PKZIP zip条目签名。该文件既是EXE,又是ZIP。 Javas ZipInputStream会读取此流吗?我不知道,但是应该。其他实现中的ZipInputStream类(例如在DotNetZip中)可以并且将其作为zip流读取。

不是很优雅,但是很可靠:

如果可以通过ZipInputStream读取Stream,则应将其压缩。

只是看起来不太好。可能是ZIP数据流损坏了吗?

@fedearne:损坏的zip流是zip流吗?

我同意:如果ZipInputStream无法读取它,则其" meant"成为一个Zip文件并不重要。对?

这是最可靠的选择。如果它损坏了,您怎么知道它是ZIP?您只需要猜测即可。

@GvS我有已压缩的流和未压缩的流。如果可以避免,我宁愿不要尝试将损坏的zip流解析为未压缩。

如果检查4个魔术字节,则4.294.967.295(完全随机)流中的1个将为误报。你负担得起吗?损坏的流是否会以魔术字节开头的非压缩流形式更频繁地发生?

由于.zip和.xlsx都具有相同的魔术数,因此我找不到有效的zip文件(如果已重命名)。

因此,我已经使用Apache Tika查找确切的文档类型。

即使将文件类型重命名为zip,它也会找到确切的类型。

参考:https://www.baeldung.com/apache-tika

检查幻数可能不是正确的选择。

Docx文件的魔术数也相似50 4B 3 4

那是因为docx文件是zip文件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值