【问题标题】:XMLEventReader from ZipInputStream closes stream来自 ZipInputStream 的 XMLEventReader 关闭流
【发布时间】:2021-03-04 19:45:46
【问题描述】:

我想流式传输包含几个非常大(~1GByte)的 XML 文件的 ZIP 文件。我可以将每个 zip 文件中的数据读入缓冲区并从中创建一个 XMLStream - 但为了节省内存,我更愿意动态处理数据。

@Test
public void zipStreamTest() throws IOException, XMLStreamException {
    FileInputStream fis = new FileInputStream("archive.zip");
    ZipInputStream zis = new ZipInputStream(fis);
    ZipEntry ei;

    while ((ei = zis.getNextEntry()) != null){      
        XMLEventReader xr = XMLInputFactory.newInstance().createXMLEventReader(zis);
        while (reader.hasNext()) {
            XMLEvent xe = xr.nextEvent();
            // do some xml event processing..
        }
        zis.closeEntry();
    }
    zis.close();
}

问题:我在执行zis.closeEntry(); 时得到java.io.IOException: Stream closed。当我删除该行时,zis.getNextEntry() 会引发相同的错误,如果它们仍然自动打开,则会关闭以前的条目。

我的 XML 流阅读器似乎在 XML 文件的末尾中断了流,因此无法处理 zip 的其余部分。

我是有实现错误还是我对流如何工作的概念不正确?

注意:要使这个最小的可复制示例,您只需要一个 zip 文件“archive.zip”,其中包含任何有效的 XML 文件(zip 中没有子目录!)。然后,您可以使用 JUnit 运行 sn-p。

【问题讨论】:

  • The implementation of XMLEventReader 不会出现在底层源代码附近,除非你显式调用它的close() 方法,所以我猜你在某个地方调用了 close()。考虑在您的问题中提供minimal reproducible example
  • 我建议使用ZipFile 而不是ZipInputStream,正如answer by Alexandra Dudkina 中所建议的那样,但是如果您正在处理数据流,例如下载,因此想继续使用ZipInputStream,您应该将它包装为CloseShieldInputStream 来自Apache Commons IO (或其他类似的帮助类)getNextEntry() 循环内。
  • @Andreas:非常感谢!您的解决方案效果很好。请制定您的解决方案作为答案,以便我可以接受。 Re:ZipFile:这是显而易见的解决方案,但我的 zip 源和目标不是文件,而是网络流和/或数据库套接字。

标签: java xml stream zip


【解决方案1】:

我建议使用ZipFile 而不是ZipInputStream,正如answer by Alexandra Dudkina 中所建议的那样。

但是,如果您正在处理数据流,例如下载,因此想继续使用ZipInputStream,您应该将它包装在CloseShieldInputStream from Apache Commons IO1 内的getNextEntry() 循环中:

while ((ei = zis.getNextEntry()) != null) {
    XMLEventReader xr = XMLInputFactory.newInstance().createXMLEventReader(new CloseShieldInputStream(zis));
    // Process XML here
    zis.closeEntry();
}

1) 或您选择的第三方库中的其他类似帮助类。

【讨论】:

    【解决方案2】:

    您可以尝试使用 java.util.zip.ZipFile 为每个条目打开单独的 InputStream:

    @Test
    public void zipStreamTest() throws Exception {
            ZipFile zipFile = new ZipFile("archive.zip");
            Iterator<? extends ZipEntry> iterator = zipFile.entries().asIterator();
            while (iterator.hasNext()) {
                ZipEntry ze = iterator.next();
                try (InputStream zis = zipFile.getInputStream(ze)) {
                    XMLEventReader reader = XMLInputFactory.newInstance().createXMLEventReader(zis);
                    while (reader.hasNext()) {
                        XMLEvent xe = reader.nextEvent();
                        // do some xml event processing
                    }
                    reader.close();
                }
            }
        }
    

    【讨论】:

    • 我从网络流中获取我的 zip,所以 ZipFile 不是一个合适的工具。 Andreas 使用 CloseShieldInputStream 找到了一个解决方案,该解决方案使用所需的方法解决了问题。
    猜你喜欢
    • 1970-01-01
    • 2023-03-26
    • 2014-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-20
    相关资源
    最近更新 更多