8

我想编写一个从单个 InputStream 读取 ZIP 中的多个 XML 文件的方法。

该方法将打开一个 ZipInputStream,并在每个 xml 文件上,获取相应的 InputStream,并将其提供给我的 XML 解析器。这是该方法的骨架:

private void readZip(InputStream is) throws IOException {

    ZipInputStream zis = new ZipInputStream(is);
    ZipEntry entry = zis.getNextEntry();

    while (entry != null) {

        if (entry.getName().endsWith(".xml")) {

            // READ THE STREAM
        }
        entry = zis.getNextEntry();
    }
}

有问题的部分是“// READ THE STREAM”。我有一个可行的解决方案,它包括创建一个 ByteArrayInputStream,并用它来提供我的解析器。但它使用缓冲区,对于大文件,我得到一个 OutOfMemoryError。这是代码,如果有人仍然感兴趣:

int count;
byte buffer[] = new byte[2048];
ByteArrayOutputStream out = new ByteArrayOutputStream();
while ((count = zis.read(buffer)) != -1) { out.write(buffer, 0, count); }       
InputStream is = new ByteArrayInputStream(out.toByteArray());

理想的解决方案是将原始 ZipInputStream 提供给解析器。它应该可以工作,因为如果我只用 Scanner 打印条目内容,它就可以工作:

Scanner sc = new Scanner(zis);
while (sc.hasNextLine())
{
    System.out.println(sc.nextLine());
}

但是...我目前正在使用的解析器(jdom2,但我也尝试过使用 javax.xml.parsers.DocumentBuilderFactory)在解析数据后关闭流:/。所以我无法获得下一个条目并继续。

所以最后的问题是:

  • 有人知道不关闭其流的 DOM 解析器吗?
  • 还有另一种方法可以从 ZipEntry 获得 InputStream 吗?

谢谢。

4

3 回答 3

6

Tim 的解决方案的一个小改进:必须在 close() 之前调用 allowToBeClosed() 的问题在于,它使得在处理异常时正确关闭 ZipInputStream 变得很棘手,并且会破坏 Java 7 的 try-with-resources 语句。

我建议创建一个包装类,如下所示:

public class UncloseableInputStream extends InputStream {
  private final InputStream input;

  public UncloseableInputStream(InputStream input) {
    this.input = input;
  }

  @Override
  public void close() throws IOException {} // do not close the wrapped stream

  @Override
  public int read() throws IOException {
    return input.read();
  }

  // delegate all other InputStream methods as with read above
}

然后可以安全地使用如下:

try (ZipInputStream zipIn = new ZipInputStream(...))
{
  DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
  ZipEntry entry;
  while (null != (entry = zipIn.getNextEntry()))
  {
    if ("file.xml".equals(entry.getName())
    {
      Document doc = db.parse(new UncloseableInputStream(zipIn));
    }
  }
}
于 2013-12-11T07:52:52.230 回答
4

感谢 halfbit,我最终得到了自己的 ZipInputStream 类,它覆盖了 close 方法:

import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipInputStream;

public class CustomZipInputStream extends ZipInputStream {

    private boolean _canBeClosed = false;

    public CustomZipInputStream(InputStream is) {
        super(is);
    }

    @Override
    public void close() throws IOException {

        if(_canBeClosed) super.close();
    }

    public void allowToBeClosed() { _canBeClosed = true; }
}
于 2013-11-16T17:01:10.750 回答
3

您可以包装 ZipInputStream 并拦截对close().

于 2013-11-16T16:50:19.637 回答