找出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文件。