Java7/Java8下ZipInputStream无法解压Zip64归档文件问题咨询
为什么Java的ZipInputStream无法处理Zip64归档文件?
你遇到的这个问题其实是Java标准库中ZipInputStream的常见局限性——虽然Java 7及以后官方宣称支持Zip64,但它的实现存在关键短板,导致无法正确处理很多实际生成的Zip64归档文件。
核心原因:ZipInputStream的读取机制限制
Zip64格式为兼容旧版Zip规范,会把大文件/大量文件的扩展元数据(比如超过4GB的文件大小、超过65535个条目等信息)放在两个位置:
- 本地文件头:紧跟在压缩数据前的小部分元数据
- 中央目录记录:归档文件末尾的全局元数据集合
Java的ZipInputStream是顺序流式读取,它只会解析每个条目开头的本地文件头。如果Zip64的扩展信息只存在于中央目录记录中(这是很多工具生成Zip64文件的默认方式),ZipInputStream就无法获取正确的条目大小、偏移量等关键信息,从而出现你看到的invalid entry size异常——它误把本地文件头中默认的0值当成了实际条目大小,而实际读取到的数据量和这个值不匹配。
而Apache Commons Compress的ZipArchiveInputStream则做了更周全的处理:它会先扫描整个归档文件的中央目录,提前获取所有条目的完整Zip64元数据,再进行流式解压,因此能正确识别并处理这类Zip64文件。
替代方案:用Java标准库的ZipFile处理Zip64
如果你不想引入第三方库,其实Java标准库中的ZipFile类是支持Zip64的(因为它是基于中央目录来读取元数据的)。可以修改你的解压逻辑,改用ZipFile实现,示例代码如下:
public static List<String> unzipFilesWithZipFile(File zipFile, File targetDirectory) { List<String> extractedFiles = new ArrayList<>(); try (ZipFile zip = new ZipFile(zipFile)) { Enumeration<? extends ZipEntry> entries = zip.entries(); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); File outputFile = new File(targetDirectory, entry.getName()); // 创建父目录 if (entry.isDirectory()) { outputFile.mkdirs(); continue; } outputFile.getParentFile().mkdirs(); // 读取并写入文件 try (InputStream in = zip.getInputStream(entry); BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(outputFile))) { byte[] buffer = new byte[8192]; int length; while ((length = in.read(buffer)) != -1) { out.write(buffer, 0, length); } extractedFiles.add(entry.getName()); } } log.debug("Total {} Files Unzipped Successfully", extractedFiles.size()); } catch (IOException e) { log.error("Error unzipping file {}", zipFile.getName(), e); } return extractedFiles; }
总结
ZipInputStream的流式读取特性导致它无法获取中央目录中的Zip64元数据,仅能处理本地文件头包含Zip64信息的归档ZipFile或Apache Commons Compress的ZipArchiveInputStream通过读取中央目录元数据,能正确处理绝大多数Zip64归档文件
内容的提问来源于stack exchange,提问作者ragi
相关产品推荐
相关产品推荐

