20

我正在尝试从 a 读取单个文件java.util.zip.ZipInputStream,并将其复制到 ajava.io.ByteArrayOutputStream中(这样我就可以创建 ajava.io.ByteArrayInputStream并将其交给最终关闭流的 3rd 方库,我不希望我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给我的第 3 方库,库将关闭流,我不能再做任何事情,比如处理剩余的文件流。

4

10 回答 10

8

您的循环看起来有效 - 以下代码(仅靠它自己)返回什么?

zipStream.read(tempBuffer)

如果它返回 -1,那么 zipStream 在你得到它之前就关闭了,所有的赌注都被关闭了。是时候使用您的调试器并确保传递给您的内容实际上是有效的。

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

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

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

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

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

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

于 2008-09-16T04:54:35.400 回答
7

您可能尝试过阅读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);
    }
}
于 2008-09-15T22:47:11.380 回答
4

我会使用来自 commons io 项目的IOUtils 。

IOUtils.copy(zipStream, byteArrayOutputStream);
于 2008-09-15T21:56:02.283 回答
4

你错过了电话

ZipEntry 条目 = (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) {
      ...
 }
于 2012-04-03T18:44:01.020 回答
3

您可以在忽略 close() 的 ZipInputStream 周围实现自己的包装器,并将其交给第三方库。

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();
    }
}
于 2008-09-16T03:31:48.343 回答
1

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

于 2008-09-16T00:19:01.020 回答
0

目前尚不清楚您是如何获得 zipStream 的。当你像这样得到它时它应该可以工作:

  zipStream = zipFile.getInputStream(zipEntry)
于 2008-09-15T21:53:13.553 回答
0

t 不清楚您是如何获得 zipStream 的。当你像这样得到它时它应该可以工作:

  zipStream = zipFile.getInputStream(zipEntry)

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

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

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

于 2008-09-15T22:56:16.260 回答
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();

 }
于 2010-01-19T11:49:11.417 回答
-1

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

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

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

于 2008-09-15T21:51:43.357 回答