从 ZipInputStream 读取到 ByteArrayOutputStream 中

2022-09-02 10:05:31

我正在尝试从 中读取单个文件,并将其复制到 a 中(这样我就可以创建一个并将其交给第三方库,该库最终将关闭流,我不希望我被关闭)。java.util.zip.ZipInputStreamjava.io.ByteArrayOutputStreamjava.io.ByteArrayInputStreamZipInputStream

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

我错过了什么,可以让我复制流?

编辑:

我应该在前面提到这不是来自文件,所以我认为我不能使用.它来自通过 servlet 上传的文件。ZipInputStreamZipFile

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


答案 1

您的循环看起来有效 - 以下代码(仅就其本身)返回什么?

zipStream.read(tempBuffer)

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

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

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

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

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

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

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


答案 2

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

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

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

您可以使用java.util.zip.ZipFile和一个库,例如Apache Commons IO的IOUtilsGuava的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);
    }
}

推荐