检查流是否为 zip 文件

6
我们需要确定一个传入的InputStream是否是zip文件或zip数据的引用,但我们没有关于流底层源的参考。我们的目标是将这个流的内容复制到一个指向另一个位置的OutputStream中。我尝试使用ZipInputStream读取流并提取ZipEntry。如果流是普通文件,则ZipEntry为空,正如预期的那样。然而,在检查ZipEntry时,我会失去流的最初几个字节。因此,当我知道流是常规流时,我已经失去了流的初始数据。有什么想法可以在不丢失数据的情况下检查InputStream是否为存档文件吗?谢谢。

请看下面我对Galactus回复的评论——这是我采取的解决方法。谢谢大家。 - AKS
1
欢迎来到 Stack Overflow!别忘了将你选择的答案标记为“已选中”(在左侧的勾形图标)。 - Greg Hewgill
5个回答

6
假设你的原始输入流没有被缓存,我建议你在检查之前将原始流包装在一个BufferedInputStream中,然后再将其包装在ZipInputStream中。你可以在BufferedInputStream中使用“mark”和“reset”来返回流的初始位置,在检查后恢复原始状态。

谢谢。我有点傻了。我马上要尝试这个。 - AKS
这个方法可行。我能够将原始的InputStream包装成BufferedInputStream,设置一个标记,然后创建ZipInputStream来检查ZipEntry。调用reset()方法后,我的流就可以被重复使用了。现在正在尝试找到最佳缓冲区大小。非常感谢! - AKS

3

这是我是如何做到的。

如果GZIPInputStream检测到不正确的zip格式(抛出ZipException),使用mark/reset来恢复流。

/**
 * Wraps the input stream with GZIPInputStream if needed. 
 * @param inputStream
 * @return
 * @throws IOException
 */
private InputStream wrapIfZip(InputStream inputStream) throws IOException {
    if (!inputStream.markSupported()) {
        inputStream = new BufferedInputStream(inputStream);
    }
    inputStream.mark(1000);
    try {
        return new GZIPInputStream(inputStream);
    } catch (ZipException e) {
        inputStream.reset();
        return inputStream;
    }
}

2

您可以检查流的前几个字节以获取ZIP本地头文件签名(PK 0x03 0x04),这对大多数情况来说已经足够了。如果您需要更精确的信息,则应该取最后的 ~100 个字节,并检查中央目录定位器字段。


是的,这似乎是验证流最明显的方法。然而,我理解,根据用于创建zip文件的工具,头文件可能会有所不同。因此,尽管这是最可靠的检查方式,但我们放弃了它,因为我们不想检查每个可能的pkzip头文件。 - AKS
请告知我,如果根据工具的不同会有不同的标题差异,是否不成立。 - AKS
2
ZIP标准对于zip局部头字段有严格的要求,因此所有的zip归档文件必须具有相同的格式。 - Nickolay Olshevsky
我真的不知道为什么这不是正确答案。你何必要做那么多工作,当你可以直接检查前四个字符呢? - PRMan
@NickolayOlshevsky,你能给我们提供一个这样验证的示例代码吗? - Cristiano
欢迎查看PKWare ZIP Appnote以了解zip归档格式。 - Nickolay Olshevsky

1

您描述了一个java.io.PushbackInputStream - 除了read(),它还有一个unread(byte[])方法,允许您将它们推回流的前面,并重新read()它们。

它在JDK1.0中就位于java.io(尽管我承认直到今天我还没有看到过它的使用)。


我确实尝试过使用PushbackInputStream。然而,创建ZipInputStream来检查流是否具有ZipEntry并因此是一个归档时,除了Pushback流的读取之外,还会读取字节 - 这些字节在未读取(unread())调用时丢失。 - AKS
@AKS:等一下,你不能在ZS中包装PBS吗?这有点让PBS失去了它的用处:( - Piskvor left the building
十年后,我确认 PushbackInputStream 可以用于这种用例。 - bric3

0

听起来有点像黑客,但你可以实现一个代理 java.io.InputStream 来坐在 ZipInputStream 和你最初传递给 ZipInputStream 构造函数的流之间。你的代理将流式传输到缓冲区,直到你知道它是 ZIP 文件还是其他文件。如果不是 ZIP 文件,则缓冲区会拯救你的一天。


是的,听起来像个黑客 :) .. 但很有趣。我即将尝试Galactus的建议,如果那个不起作用,我会尝试这个 :) - AKS

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接