![](/img/trans.png)
[英]Java.util.zip.ZipException: invalid entry size (expected 18401 but got 0 bytes) when using putNextEntry() in ZipOutputStream?
[英]Stream Zip files: java.util.zip.ZipException: invalid entry size (expected 0 but got 419 bytes)
我有一个 web 服务处理 ZIP 文件的内容,我从 a.network 源和 stream 收到动态返回 a.network 目标。 这对我大约 60% 的测试文件非常有用,但其中 40% 无法处理,因为zipEntry.getSize()
返回-1
作为所有 zip 条目的文件大小。
下面您可以看到两个 java 测试将内容从源 zip 流式传输到目标 zip。第一个接受任何InputStream
作为源(这是我需要的,因为我直接从 .network 获取数据)并且无法处理 zip 条目大小未知 ( -1
)。
第二个测试知道如何处理未知 ( -1
) 大小的条目,但只能处理源自本地文件的流(这不是我需要的 - 它只是为了证明,有问题的 zip 文件没有损坏) .
网上有很多处理本地 zip 文件的示例 - 但很少有关于处理 .network 流的示例,这就是为什么我很难找到解决方案的原因。
第一个例子抛出的错误是Stream Zip files: java.util.zip.ZipException: invalid entry size (expected 0 but got 419 bytes)
这是我的代码:
package de.ftk.threemf.mesh;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.Test;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Date;
import java.util.Enumeration;
import java.util.zip.*;
@Slf4j
public class ZipStreamTests {
@Test
public void generalizedStreamZipTest() throws IOException {
Path path = Path.of("testdata/brokenzip/trex.3mf");
InputStream in = Files.newInputStream(path);
OutputStream out = Files.newOutputStream(Path.of("testoutput/ziptest.3mf"));
ZipInputStream zipInputStream = new ZipInputStream(in);
ZipEntry zipEntry;
CheckedOutputStream checkedOutputStream = new CheckedOutputStream(out, new Adler32());
ZipOutputStream zipOutputStream = new ZipOutputStream(checkedOutputStream);
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
log.info("zip file contains: {} modified on {}", zipEntry.getName(), new Date(zipEntry.getTime()));
zipOutputStream.putNextEntry(zipEntry);
log.info("expecting " + zipEntry.getSize() + " bytes");
IOUtils.copy(zipInputStream, zipOutputStream);
zipOutputStream.closeEntry();
zipInputStream.closeEntry();
}
zipInputStream.close();
zipOutputStream.finish();
zipOutputStream.close();
in.close();
out.close();
}
@Test
public void fileStreamZipTest() throws IOException {
ZipFile zipFile = new ZipFile("testdata/brokenzip/trex.3mf");
final ZipOutputStream zos = new ZipOutputStream(new FileOutputStream("testoutput/ziptest.3mf"));
for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
ZipEntry entryIn = e.nextElement();
log.info("zip file contains: {} modified on {}", entryIn.getName(), new Date(entryIn.getTime()));
ZipEntry zipEntry = new ZipEntry(entryIn.getName());
log.info("expecting " + zipEntry.getSize() + " bytes");
zos.putNextEntry(zipEntry);
InputStream is = zipFile.getInputStream(entryIn);
byte[] buf = new byte[1024];
int len;
while ((len = (is.read(buf))) > 0) {
zos.write(buf);
}
zos.closeEntry();
}
zos.close();
}
}
提示: 3MF
文件是一个包含 3D 模型的ZIP
文件。
这与 ZIP64 子格式https://www.ibm.com/support/pages/zip-file-unreadable-cause-javautilzipzipexception-invalid-entry-size有关
较新的 java7 和 java8 版本已修复此问题 - jdk-1.8.0_91 不正常,openjdk-1.8.0.212.b04 正常。
即使是最近的 jdk-1.8.0_341, ZipInputStream
中似乎仍然存在与 ZIP64相关的错误。
它在这些条件下触发:
ZipInputStream
将数据描述符解释为 32 位,尽管本地文件 header 是 ZIP64,因此读取文件大小不正确。 罪魁祸首在readEnd()
方法中:
if ((flag & 8) == 8) {
/* "Data Descriptor" present */
if (inf.getBytesWritten() > ZIP64_MAGICVAL ||
inf.getBytesRead() > ZIP64_MAGICVAL) {
// ZIP64 format
...
错误修复应如下所示:
if ((flag & 8) == 8) {
/* "Data Descriptor" present */
if (/* file header was ZIP64*/) {
// ZIP64 format
...
只有在读取所有文件数据并且ZipInputStream
尝试关闭并验证ZipEntry
大小和 CRC 时才会抛出异常。 因此 stream 可以使用反射手动推进以从错误中恢复。
ZipInputStream zipStream = ...;
zipStream.getNextEntry();
try {
// Read file data
zipStream.read(...);
} catch (ZipException e) {
// Recover from error condition
if (e.getMessage().startsWith("invalid entry size (expected 0 ")) {
// Grab pushback stream
Field inF = FilterInputStream.class.getDeclaredField("in"); inF.setAccessible(true);
PushbackInputStream in = (PushbackInputStream) inF.get(zipStream);
for (int i = 0; i < 8; i++) in.read(); // Read 8 extra bytes to compensate footer
// Close the entry manually
Field f = ZipInputStream.class.getDeclaredField("entryEOF");
f.setAccessible(true);
f.set(zipStream, true);
f = ZipInputStream.class.getDeclaredField("entry");
f.setAccessible(true);
f.set(zipStream, null);
} else {
throw e;
}
}
zipStream.getNextEntry(); // Continue as if exception hadn't occured
我在这个存储库中创建了一个完整的打包解决方案。 还有一些正在测试资源的示例 ZIP 会触发该错误。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.