从ZipInputStream读取到ByteArrayOutputStream
我正在尝试从java.util.zip.ZipInputStream中读取一个文件,然后将其复制到java.io.ByteArrayOutputStream中(这样我就可以创建一个java.io.ByteArrayInputStream并将其交给到最终关闭流的第3方库,并且我不希望我的ZipInputStream
关闭)。
我可能在这里错过了一些基本的东西,但是我从来没有在这里进入while循环:
ByteArrayOutputStream streamBuilder = new ByteArrayOutputStream(); int bytesRead; byte[] tempBuffer = new byte[8192*2]; try { while ((bytesRead = zipStream.read(tempBuffer)) != -1) { streamBuilder.write(tempBuffer, 0, bytesRead); } } catch (IOException e) { // ... }
我缺少什么让我可以复制信息流?
编辑:
我应该早先提到过,这个" ZipInputStream"不是来自文件,所以我认为我不能使用" ZipFile"。它来自通过Servlet上传的文件。
另外,在进入此代码段之前,我已经在ZipInputStream
上调用了getNextEntry()
。如果我不尝试将文件复制到另一个InputStream
中(通过上面提到的OutputStream
),而只是将ZipInputStream
传递给我的第3方库,则该库将关闭流,而我什么也不能做更多,例如处理流中剩余的文件。
解决方案
回答
检查输入流是否位于请求中。
否则,作为实现:除非我们在另一个线程中处理此确切的流,否则我不需要在读取时就写入结果流。
只需创建一个字节数组,读取输入流,然后创建输出流即可。
回答
目前尚不清楚我们如何获得zipStream。像这样得到它应该可以工作:
zipStream = zipFile.getInputStream(zipEntry)
回答
我会使用commons io项目中的IOUtils。
IOUtils.copy(zipStream, byteArrayOutputStream);
回答
我们可能试图像这样从FileInputStream
中读取:
ZipInputStream in = new ZipInputStream(new FileInputStream(...));
由于zip归档文件可以包含多个文件,因此我们无法使用此文件,因此我们需要指定要读取的文件。
我们可以使用java.util.zip.ZipFile和一个库(例如来自Apache Commons IO的IOUtils或者来自Guava的ByteStreams)来复制流。
例子:
ByteArrayOutputStream out = new ByteArrayOutputStream(); try (ZipFile zipFile = new ZipFile("foo.zip")) { ZipEntry zipEntry = zipFile.getEntry("fileInTheZip.txt"); try (InputStream in = zipFile.getInputStream(zipEntry)) { IOUtils.copy(in, out); } }
回答
尚不清楚我们如何获得zipStream。像这样得到它应该可以工作:
zipStream = zipFile.getInputStream(zipEntry)
如果要从ZipFile获取ZipInputStream,则可以为3d方库获取一个流,让其使用它,然后使用之前的代码获取另一个输入流。
记住,输入流是一个游标。如果我们拥有全部数据(如ZipFile),则可以在其上请求N个游标。
不同的情况是,如果只有" GZip"输入流,则只有压缩字节流。在那种情况下,ByteArrayOutputStream缓冲区很有意义。
回答
我会在ZipInputStream上调用getNextEntry(),直到它位于所需的条目为止(使用ZipEntry.getName()等)。调用getNextEntry()会将"光标"前进到它返回的条目的开头。然后,使用ZipEntry.getSize()确定应使用zipInputStream.read()读取多少个字节。
回答
我们可以围绕ZipInputStream实现自己的包装程序,该包装程序将忽略close()并将其交给第三方库。
thirdPartyLib.handleZipData(new CloseIgnoringInputStream(zipStream)); class CloseIgnoringInputStream extends InputStream { private ZipInputStream stream; public CloseIgnoringInputStream(ZipInputStream inStream) { stream = inStream; } public int read() throws IOException { return stream.read(); } public void close() { //ignore } public void reallyClose() throws IOException { stream.close(); } }
回答
循环看起来有效,以下代码(仅凭其返回)返回什么?
zipStream.read(tempBuffer)
如果返回-1,则在获取zipStream之前将其关闭,并且所有赌注均已关闭。现在该使用调试器并确保传递给内容实际上是有效的了。
当我们调用getNextEntry()时,它是否返回一个值,并且该条目中的数据是否有意义(即getCompressedSize()是否返回一个有效值)?如果我们只是在读取一个未嵌入预读zip条目的Zip文件,则ZipInputStream将无法为我们工作。
有关Zip格式的一些有用的花絮:
嵌入zip文件中的每个文件都有一个标头。此标头可以包含有用的信息(例如,流的压缩长度,在文件中的偏移量,CRC),也可以包含一些魔术值,这些魔术值基本上说"信息不在流标头中,我们必须检查邮编后同步码"。
然后,每个zip文件都有一个表,该表添加到文件末尾,该表包含所有zip条目以及实际数据。最后的表是必填项,其中的值必须正确。相反,不必提供嵌入在流中的值。
如果使用ZipFile,它将读取zip末尾的表。如果使用ZipInputStream,我怀疑getNextEntry()会尝试使用流中嵌入的条目。如果未指定这些值,则ZipInputStream不知道该流可能有多长时间。膨胀算法是自终止的(实际上,我们不需要知道输出流的未压缩长度即可完全恢复输出),但是该阅读器的Java版本可能无法很好地处理这种情况。
我要说的是,有一个servlet返回ZipInputStream是非常不寻常的(如果我们要接收压缩的内容,则接收inflatorInputStream更为常见。
回答
请尝试以下代码
private static byte[] getZipArchiveContent(File zipName) throws WorkflowServiceBusinessException { BufferedInputStream buffer = null; FileInputStream fileStream = null; ByteArrayOutputStream byteOut = null; byte data[] = new byte[BUFFER]; try { try { fileStream = new FileInputStream(zipName); buffer = new BufferedInputStream(fileStream); byteOut = new ByteArrayOutputStream(); int count; while((count = buffer.read(data, 0, BUFFER)) != -1) { byteOut.write(data, 0, count); } } catch(Exception e) { throw new WorkflowServiceBusinessException(e.getMessage(), e); } finally { if(null != fileStream) { fileStream.close(); } if(null != buffer) { buffer.close(); } if(null != byteOut) { byteOut.close(); } } } catch(Exception e) { throw new WorkflowServiceBusinessException(e.getMessage(), e); } return byteOut.toByteArray(); }