应使用Apache Commons Compress的ZipArchiveInputStream并显式指定GBK等编码,或升级JDK至8u20+以支持ZIP64;避免用available()判断流结束,注意目录创建与路径穿越风险。

ZipInputStream读取中文文件名乱码怎么办
Java原生ZipInputStream不支持GBK、GB2312等中文编码,默认按UTF-8解码文件名,遇到老系统打包的ZIP(尤其是Windows下用WinRAR/7-Zip默认GBK)会返回?????.txt或抛IllegalArgumentException。
解决思路不是“修复ZipInputStream”,而是绕过它——改用支持编码指定的第三方库,或在读取前预处理字节数组。JDK 7+ 的java.util.zip.ZipFile配合Charset参数仍不可用,真正可用的是:
-
org.apache.commons:commons-compress(推荐),其ZipArchiveInputStream允许传入Charset -
net.sf.sevenzipjbinding(复杂,适合需要7z/LZMA场景) - 手动对
ZipEntry.getName().getBytes(StandardCharsets.UTF_8)做编码转换(高风险,仅限已知原始编码且无特殊字符)
示例:用commons-compress读GBK编码ZIP
ZipArchiveInputStream zis = new ZipArchiveInputStream(
new FileInputStream("test_gbk.zip"),
"GBK", // 显式指定编码
true // skipBytesForExtraField
);
ZipArchiveEntry entry;
while ((entry = zis.getNextZipEntry()) != null) {
System.out.println(entry.getName()); // 正确输出中文名
IOUtils.copy(zis, new FileOutputStream(entry.getName()));
}
ZipInputStream无法正确识别ZIP64扩展项
当ZIP文件中单个文件 > 4GB 或总条目数 > 65535,必须启用ZIP64格式。但JDK 6/7的ZipInputStream默认不识别ZIP64的central directory locator,会抛ZipException: invalid CEN header (invalid zip64 extra data)或直接跳过后续条目。
立即学习“Java免费学习笔记(深入)”;
Dbsite企业网站管理系统V1.5.0 秉承"大道至简 邦达天下"的设计理念,以灵巧、简单的架构模式构建本管理系统。可根据需求可配置多种类型数据库(当前压缩包支持Access).系统是对多年企业网站设计经验的总结。特别适合于中小型企业网站建设使用。压缩包内包含通用企业网站模板一套,可以用来了解系统标签和设计网站使用。QQ技术交流群:115197646 系统特点:1.数据与页
该问题在JDK 8u20+已修复,但仍有遗留环境运行旧JRE。验证方式:unzip -l broken.zip若提示zip64 end of central directory locator即为ZIP64。
- JDK 8u20及以上:原生
ZipInputStream可安全使用 - JDK 7或更早:必须升级JRE,或改用
commons-compress(从1.13起完整支持ZIP64) - 避免用
ZipInputStream.available()判断流是否结束——它在ZIP64下始终返回0,应依赖getNextEntry() == null
ZipInputStream与ZipFile性能和资源管理差异
很多人误以为ZipInputStream比ZipFile“更轻量”,其实相反:ZipInputStream是纯顺序读,无法随机访问;而ZipFile会将central directory加载进内存,支持getEntry("a/b.txt")直接定位,适合需多次查找特定文件的场景。
-
ZipInputStream:适合单次遍历、流式解压(如HTTP响应体直解)、内存受限环境 -
ZipFile:适合需随机读取、校验某几个文件、或提前获取所有条目元数据(size、time)的场景 -
ZipFile必须显式调用close(),否则底层RandomAccessFile句柄泄露;ZipInputStream也需关闭,但漏关只影响当前流 - 二者都不支持边写边读ZIP——要生成ZIP请用
ZipOutputStream
用ZipInputStream解压时跳过目录条目还是保留?
ZipInputStream读到的ZipEntry可能代表目录(entry.isDirectory() == true),也可能只是普通文件。是否创建对应目录,取决于你的业务逻辑。
- 大多数解压工具(如unzip命令)默认创建目录结构,所以代码中应检查
entry.isDirectory()并mkdirs() - 若目标路径已存在同名文件,
new File(entry.getName()).mkdirs()会静默失败,需提前delete()或跳过 - 注意路径穿越风险:
entry.getName()可能是../../etc/passwd,务必用FilenameUtils.normalize()(commons-io)或手动校验路径是否以"../"开头 - 不要依赖
entry.getSize() == 0判断目录——有些ZIP工具打空目录时会设非零size
真正的难点不在代码怎么写,而在你是否清楚这个ZIP是谁打的、用什么工具、在什么系统上、有没有隐藏属性——这些信息缺失时,光靠ZipInputStream本身无法还原原始意图。









