用Java解压缩内存中的ZIP文件

时间:2012-08-19 23:08:27

标签: java zip

我正在下载包含XML的压缩文件,我想避免在操作它们之前将zip文件写入磁盘,因为延迟要求。但是,java.util.zip对我来说还不够。没有办法说“这是一个zip文件的字节数组,使用它”而不将其转换为流,ZipInputStream不可靠,因为它会扫描条目标题(请参阅下面的编辑,了解为什么会这样做不可靠)。

我还没有访问我将要处理的zip文件,因此我不知道我是否能够通过ZipInputStream处理它们,我需要找到一个解决方案将与任何有效的ZIP文件一起使用,因为一旦我投入生产,失败的惩罚就会很高。

假设ZipInputStream不起作用,在没有条目标题的情况下,我该怎么做才能解决这个问题?我正在使用Wikipedia's definition,其中包含有关如何正确解压缩zip文件(下面引用)的注释,作为标准。

修改

Apache Commons Zip库对使用Stream(他们的解决方案和Java)的一些问题有一个good write up。我将进一步补充,从维基百科和个人经验来看,条目标题上的大小和crc字段可能无法填充(我在这些字段中的文件为-1)。感谢centic提供此链接。

另外,让我引用维基百科的主题:

  

正确阅读zip档案的工具必须扫描签名   各个字段,zip中心目录。他们一定不能扫描   条目,因为只有目录指定文件块的位置   开始。扫描可能会导致误报,因为格式不会   禁止其他数据在块或未压缩的流之间   包含此类签名。

请注意ZipInputStream扫描条目,而不是中心目录,这是它的问题。

最终修改

如果有人感兴趣,可以使用this script生成有效的ZIP文件,ZipInputStream无法从现有的ZIP文件中读取该文件。所以,作为这个封闭问题的最终编辑,我需要一个可以读取文件的库,例如这个脚本生成的文件。

4 个答案:

答案 0 :(得分:22)

编辑:另一个建议......

从Apache Commons实现中查看ZipFile,看起来很难难以有效地为您的项目分叉。在你的字节数组周围创建一个包装器,它包含所需的RandomAccessFile API的所有部分(我不认为有很多)。您已经表示您更喜欢ZipFile的界面,那么为什么不选择呢?

我们对您的项目知之甚少,不知道这是否会引发任何法律问题 - 即使您提供了详细信息,我怀疑这里的任何人都能提供良好的法律建议 - 但我怀疑它不会花费一两个多小时的时间来解决这个问题,并且我怀疑你对此有合理的信心。


编辑:这可能是一个稍微有效的答案......

如果您担心这些条目不是连续的,但又不想自己处理所有压缩方面,您可能会考虑一个有效重写数据的选项。创建一个新的ByteArrayOutputStream,并在最后阅读中心目录。对于中央目录中的每个条目,以您认为ZipInputStream将满意的格式向输出流写出条目(标题+数据)。然后编写一个新的中央目录 - 如果您希望您的替换有效,您可能需要从头开始执行此操作,但如果您正在使用您知道赢得的代码,那么实际上中央目录,您可以只提供原始目录,忽略它可能不会有效的事实。只要从正确的签名开始,这可能就足够了:)。

完成后,将ByteArrayOutputStream转换为 new byte[],将其换成ByteArrayInputStream,然后将其传递给{ {1}}或ZipInputStream

根据您的目的,您可能甚至不需要做那么多 - 您可以通过创建" mini"来提取每个文件。 zip文件,只包含您一次从目录中读取的一个条目。

这个确实涉及了解zip文件格式,但并不完全 - 只是骨架,有效。它不是一个快速简单的解决方案,比如完全使用现有的API,但它不应该非常。它并不能保证它能够读取所有无效文件(它怎么可能?)但它会保护你免受条目之间的数据和#34;问题你似乎特别关注。希望它至少是一个有用的想法...


  

没有办法说"这里是一个zip文件的字节数组,使用它"

是的,有:

ZipArchiveInputStream

这就留下了byte[] data = ...; ByteArrayInputStream byteStream = new ByteArrayInputStream(data); ZipInputStream zipStream = new ZipInputStream(byteStream); 是否可以处理你所提供的所有zip文件的问题 - 但我不会那么快就把它写下来。

当然,还有其他可用的API。例如,您可能需要查看Apache Commons Compress。即使ZipInputStream需要文件,ZipArchiveInputStream也没有 - 所以再次使用ZipFile。编辑:看起来ByteArrayInputStream 不会从中央目录中读取。我希望它会事先使用ZipArchiveStream进行检查,但似乎不是......

编辑:在对该问题的评论中,我问你在哪里读到zip文件不必包含条目数据。你引用维基百科:

  

"正确读取zip存档的工具必须扫描各个字段的签名,即zip中心目录。它们不能扫描条目,因为只有目录指定文件块的开始位置。扫描可能会导致误报,因为格式不禁止其他数据在块之间或包含此类签名的未压缩流中。"

这与输入数据是可选的不同。它说在尴尬的地方可能有额外的数据,而不是条目可能完全丢失。它基本上说这些条目不应该被认为是连续的。我很高兴地承认markSupported可能没有读取文件末尾的中心目录,但查找代码与查找不存在的条目数据的代码相同。

然后写下:

  

我可能会进一步补充说,拉链是否有效并不是我的担忧。使用它是。

...这表明你想要处理无效zip文件的代码。结合这个:

  

我还无法访问我要处理的zip文件,所以我不知道我是否能够通过流处理它们

这意味着您要求的代码应该处理zip文件,这些文件在您甚至无法预测的情况下都是无效的。你能够拒绝它有多么无效?如果我给你1000个随机字节,而根本没有尝试将它们作为一个zip文件,你会用它做什么呢?

基本上,您需要更紧密地解决问题,然后才能确定特定库是否是有效的解决方案。从各个地方收集一组zip文件是合理的,这些文件可能以易于理解的方式无效,并说“我必须能够支持所有这些。”#34;如果事实证明那不够好,那么可能需要做一些工作。但是,为了能够支持任何事情,无论多么破碎,根本不是一个有效的要求。

答案 1 :(得分:2)

TrueZIP库提供了替代的成熟zip实现。

它还具有file system abstraction even for HTTP

例如:

Path path = new TPath(new URI("http://acme.com/download/everything.zip/entry.xml"));
try (InputStream in = Files.newInputStream(path)) {
    // Read archive entry contents here.
    ...
}

因此,如果您只对特定条目感兴趣,它将仅下载它们,从而节省带宽和时间。 而且您不必编写下载代码。

另见http://truezip.java.net/faq.html#http

答案 2 :(得分:2)

我会使用Apache库commons-compress,请参阅http://commons.apache.org/compress/

它支持通过流读取Zip文件,http://commons.apache.org/compress/zip.html上有详细的文档,有详细的文档。它还说明了Zip格式中固有的一些限制。

示例代码如下所示:

ZipArchiveInputStream zip =
    new ZipArchiveInputStream(inputStream);
try {
    ZipArchiveEntry entry = zip.getNextZipEntry();
    while(entry != null) {
        assertEquals("README", entry.getName());
        ...
        entry = zip.getNextZipEntry();
    }
} finally {
    zip.close();
}

答案 3 :(得分:1)

这个问题听起来与How to create a directory in memory? pseudo file system / virtual directory类似。基本上,我的建议是使用更通用的解决方案 - 内存中的虚拟文件系统(我不是指在操作系统级别,如Linux的ramfs / tmpfs)。

一个例子是使用Java 7 NIO API,它现在提供了一个SPI,用于通过FileSystemProvider实现文件系统。似乎ShrinkWrap文件系统实现了这个SPI。

更容易使用的选项是使用Apache Commons VFS'ram filesystem:它只需要Java 5.如果你需要与Java 5和6兼容,这可能是你最好的选择。

我首先记得从this article读取Java中的内存中文件系统,除了指出Commons VFS和JBoss Microcontainer之类的解决方案之外,还为NetBeans IDE提供了一个很好的示例用例。

虽然内存中的虚拟文件系统是避免操作系统级文件系统的一个很好的通用解决方案(具有相关的性能优势),但它可能还有其他缺点,更专业的解决方案可以解决这些缺点。例如,我不确定当从多个线程同时使用时,如何使用此文件系统。只要您不访问相同的文件,或者您可能需要创建单独的文件系统(在资源使用方面可能过高),它可能正常工作。