从ZipInputStream读取到ByteArrayOutputStream

时间:2020-03-05 18:54:37  来源:igfitidea点击:

我正在尝试从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();

 }