从ZipInputStream读取到ByteArrayOutputStream

时间:2008-09-15 21:41:21

标签: java zipinputstream zipoutputstream

我正在尝试从java.util.zip.ZipInputStream读取单个文件,并将其复制到java.io.ByteArrayOutputStream(这样我就可以创建java.io.ByteArrayInputStream并将其交给第三方库这将最终关闭流,我不希望我的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上传的文件。

此外,在获取此代码段之前,我已在getNextEntry()上调用ZipInputStream。如果我不尝试将文件复制到另一个InputStream(通过上面提到的OutputStream),并将ZipInputStream传递给我的第三方库,则库会关闭该流,并且我无法做更多的事情,比如处理流中剩余的文件。

10 个答案:

答案 0 :(得分:7)

您可能尝试过这样的FileInputStream阅读:

ZipInputStream in = new ZipInputStream(new FileInputStream(...));

不会有效,因为zip存档可以包含多个文件,您需要指定要读取的文件。

您可以使用java.util.zip.ZipFile以及IOUtils from Apache Commons IOByteStreams from Guava等可帮助您复制流的库。

示例:

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);
    }
}

答案 1 :(得分:7)

你的循环看起来有效 - 下面的代码(只是它自己的代码)会返回什么?

zipStream.read(tempBuffer)

如果它返回-1,那么zipStream会在你获得它之前关闭,所有的赌注都会关闭。现在是时候使用你的调试器,并确保传递给你的是真正有效的。

当你调用getNextEntry()时,它是否返回一个值,并且条目中的数据是否有意义(即getCompressedSize()是否返回有效值)?如果您只是在阅读没有嵌入预读zip条目的Zip文件,那么ZipInputStream将不适合您。

关于Zip格式的一些有用的花絮:

zip文件中嵌入的每个文件都有一个标题。此标头可以包含有用的信息(例如流的压缩长度,它在文件中的偏移量,CRC) - 或者它可以包含一些基本上说'信息不在流标题中的魔术值,你必须检查Zip post-amble'。

每个zip文件都有一个附加到文件末尾的表,其中包含所有zip条目以及实际数据。最后的表是必需的,其中的值必须正确。相反,不必提供流中嵌入的值。

如果您使用ZipFile,它会读取zip末尾的表格。如果您使用ZipInputStream,我怀疑getNextEntry()尝试使用流中嵌入的条目。如果未指定这些值,则ZipInputStream不知道流可能有多长。膨胀算法是自终止的(实际上你不需要知道输出流的未压缩长度以便完全恢复输出),但是这个阅读器的Java版本可能不能很好地处理这种情况。

我会说让servlet返回一个ZipInputStream是相当不寻常的(如果你要接收压缩内容,接收inflatorInputStream会更常见。

答案 2 :(得分:4)

我会从项目的公共场所使用IOUtils

IOUtils.copy(zipStream, byteArrayOutputStream);

答案 3 :(得分:4)

你缺少电话

ZipEntry entry =(ZipEntry)zipStream.getNextEntry();

定位第一个条目解压缩的第一个字节。

 ByteArrayOutputStream streamBuilder = new ByteArrayOutputStream();
 int bytesRead;
 byte[] tempBuffer = new byte[8192*2];
 ZipEntry entry = (ZipEntry) zipStream.getNextEntry();
 try {
     while ( (bytesRead = zipStream.read(tempBuffer)) != -1 ){
        streamBuilder.write(tempBuffer, 0, bytesRead);
     }
 } catch (IOException e) {
      ...
 }

答案 4 :(得分:3)

您可以在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();
    }
}

答案 5 :(得分:1)

我会在ZipInputStream上调用getNextEntry(),直到它位于您想要的条目(使用ZipEntry.getName()等)。调用getNextEntry()会将“游标”前进到它返回的条目的开头。然后,使用ZipEntry.getSize()来确定使用zipInputStream.read()读取的字节数。

答案 6 :(得分:0)

目前还不清楚你是如何得到zipStream的。当你得到它时,它应该工作:

  zipStream = zipFile.getInputStream(zipEntry)

答案 7 :(得分:0)

目前还不清楚你是如何得到zipStream的。当你得到它时,它应该工作:

  zipStream = zipFile.getInputStream(zipEntry)

如果您从ZipFile获取ZipInputStream,您可以获得一个3d方库的流,让它使用它,然后使用之前的代码获得另一个输入流。

请记住,输入流是一个游标。如果您拥有整个数据(如ZipFile),则可以请求N个游标。

不同的情况是,如果您只有一个“GZip”输入流,只有一个压缩字节流。在这种情况下,ByteArrayOutputStream缓冲区是有意义的。

答案 8 :(得分:0)

请尝试下面的代码

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();

 }

答案 9 :(得分:-1)

检查输入流是否位于乞讨中。

否则,作为实现:我不认为你在阅读时需要写入结果流,除非你在另一个线程中处理这个确切的流。

只需创建一个字节数组,读取输入流,然后创建输出流。