Java从ZipInputStream条目创建InputStream

我想编写一个方法,从一个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 sc = new Scanner(zis); while (sc.hasNextLine()) { System.out.println(sc.nextLine()); } 

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

最后问题是:

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

谢谢。

您可以包装ZipInputStream并拦截对close()的调用。

感谢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; } } 

Tim解决方案的一个小改进:必须在close()之前调用allowToBeClosed()的问题是它在处理exception棘手时正确关闭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)); } } }