Java 从 ZipInputStream 读取到 ByteArrayOutputStream

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/67275/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 07:52:58  来源:igfitidea点击:

Reading from a ZipInputStream into a ByteArrayOutputStream

javazipinputstreamzipoutputstream

提问by pkaeding

I am trying to read a single file from a java.util.zip.ZipInputStream, and copy it into a java.io.ByteArrayOutputStream(so that I can then create a java.io.ByteArrayInputStreamand hand that to a 3rd party library that will end up closing the stream, and I don't want my ZipInputStreamgetting closed).

我正在尝试从 a 读取单个文件java.util.zip.ZipInputStream,并将其复制到 a java.io.ByteArrayOutputStream(以便我可以创建一个java.io.ByteArrayInputStream并将其交给最终关闭流的第 3 方库,我不希望我ZipInputStream被关闭) .

I'm probably missing something basic here, but I never enter the while loop here:

我可能在这里遗漏了一些基本的东西,但我从来没有在这里进入 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) {
    // ...
}

What am I missing that will allow me to copy the stream?

我缺少什么可以让我复制流?

Edit:

编辑:

I should have mentioned earlier that this ZipInputStreamis not coming from a file, so I don't think I can use a ZipFile. It is coming from a file uploaded through a servlet.

我应该早些时候提到这ZipInputStream不是来自文件,所以我认为我不能使用ZipFile. 它来自通过 servlet 上传的文件。

Also, I have already called getNextEntry()on the ZipInputStreambefore getting to this snippet of code. If I don't try copying the file into another InputStream(via the OutputStreammentioned above), and just pass the ZipInputStreamto my 3rd party library, the library closes the stream, and I can't do anything more, like dealing with the remaining files in the stream.

此外,我已经在获得这段代码之前调用getNextEntry()ZipInputStream。如果我不尝试将文件复制到另一个文件中InputStream(通过OutputStream上面提到的),而只是将文件传递ZipInputStream给我的第 3 方库,则该库将关闭流,并且我不能再做任何事情,例如处理中的剩余文件流。

采纳答案by Kevin Day

Your loop looks valid - what does the following code (just on it's own) return?

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

zipStream.read(tempBuffer)

if it's returning -1, then the zipStream is closed before you get it, and all bets are off. It's time to use your debugger and make sure what's being passed to you is actually valid.

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

When you call getNextEntry(), does it return a value, and is the data in the entry meaningful (i.e. does getCompressedSize() return a valid value)? IF you are just reading a Zip file that doesn't have read-ahead zip entries embedded, then ZipInputStream isn't going to work for you.

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

Some useful tidbits about the Zip format:

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

Each file embedded in a zip file has a header. This header can contain useful information (such as the compressed length of the stream, it's offset in the file, CRC) - or it can contain some magic values that basically say 'The information isn't in the stream header, you have to check the Zip post-amble'.

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

Each zip file then has a table that is attached to the end of the file that contains all of the zip entries, along with the real data. The table at the end is mandatory, and the values in it must be correct. In contrast, the values embedded in the stream do not have to be provided.

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

If you use ZipFile, it reads the table at the end of the zip. If you use ZipInputStream, I suspect that getNextEntry() attempts to use the entries embedded in the stream. If those values aren't specified, then ZipInputStream has no idea how long the stream might be. The inflate algorithm is self terminating (you actually don't need to know the uncompressed length of the output stream in order to fully recover the output), but it's possible that the Java version of this reader doesn't handle this situation very well.

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

I will say that it's fairly unusual to have a servlet returning a ZipInputStream (it's much more common to receive an inflatorInputStream if you are going to be receiving compressed content.

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

回答by Sunny Milenov

Check if the input stream is positioned in the begging.

检查输入流是否定位在begging中。

Otherwise, as implementation: I do not think that you need to write to the result stream while you are reading, unless you process this exact stream in another thread.

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

Just create a byte array, read the input stream, then create the output stream.

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

回答by Boris Bokowski

It is unclear how you got the zipStream. It should work when you get it like this:

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

  zipStream = zipFile.getInputStream(zipEntry)

回答by ScArcher2

I'd use IOUtilsfrom the commons io project.

我会使用commons io 项目中的IOUtils

IOUtils.copy(zipStream, byteArrayOutputStream);

回答by Benedikt Waldvogel

You probably tried reading from a FileInputStreamlike this:

您可能尝试从FileInputStream这样的读取:

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

This won'twork since a zip archive can contain multiple files and you need to specify which file to read.

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

You could use java.util.zip.ZipFileand a library such as IOUtils from Apache Commons IOor ByteStreams from Guavathat assist you in copying the stream.

您可以使用java.util.zip.ZipFile和诸如来自 Apache Commons IO 的 IOUtils来自 Guava 的ByteStreams 之类的库来帮助您复制流。

Example:

例子:

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

回答by helios

t is unclear how you got the zipStream. It should work when you get it like this:

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

  zipStream = zipFile.getInputStream(zipEntry)

If you are obtaining the ZipInputStream from a ZipFile you can get one stream for the 3d party library, let it use it, and you obtain another input stream using the code before.

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

Remember, an inputstream is a cursor. If you have the entire data (like a ZipFile) you can ask for N cursors over it.

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

A diferent case is if you only have an "GZip" inputstream, only an zipped byte stream. In that case you ByteArrayOutputStream buffer makes all sense.

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

回答by Boris Bokowski

I would call getNextEntry() on the ZipInputStream until it is at the entry you want (use ZipEntry.getName() etc.). Calling getNextEntry() will advance the "cursor" to the beginning of the entry that it returns. Then, use ZipEntry.getSize() to determine how many bytes you should read using zipInputStream.read().

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

回答by jt.

You could implement your own wrapper around the ZipInputStream that ignores close() and hand that off to the third-party library.

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

回答by Dmytro

Please try code bellow

请尝试以下代码

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

 }

回答by Juan Ignacio

You're missing call

你错过了电话

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

ZipEntry 条目 = (ZipEntry) zipStream.getNextEntry();

to position the first byte decompressed of the first entry.

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

 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) {
      ...
 }