Java ZipInputStream 默认用平台编码解码ZIP中文名导致乱码,因JDK不支持显式指定编码;推荐用Apache Commons Compress并传入UTF-8编码,或生成ZIP时启用UTF-8 flag。

Java 中 ZipInputStream 默认使用平台默认编码(如 Windows 上是 GBK)解码 ZIP 文件中的文件名,而大多数现代压缩工具(如 WinRAR、7-Zip、macOS 归档工具)在生成 ZIP 时会用 UTF-8 编码存储中文文件名。但标准 JDK 的 ZipInputStream 不支持显式指定文件名编码,导致读取时乱码。
根本原因:ZIP 规范与 JDK 实现限制
ZIP 文件格式本身没有强制规定文件名编码,传统 ZIP 使用 IBM Code Page 437;而现代工具常通过“通用位标志第11位”(UTF-8 flag)标识文件名是 UTF-8 编码。但直到 Java 7,java.util.zip 包仍不识别该标志;Java 7+ 虽部分支持(需系统属性 sun.zip.encoding),但不可靠且非标准。
推荐方案:改用 Apache Commons Compress
这是最稳定、跨平台、支持显式编码的解决方案。它完全兼容 ZIP 格式,并允许构造时传入字符集。
- 添加 Maven 依赖:
- 读取 ZIP 并指定 UTF-8 编码:
new FileInputStream("zh-files.zip"), "UTF-8", true)) {
ZipArchiveEntry entry;
while ((entry = (ZipArchiveEntry) ais.getNextEntry()) != null) {
System.out.println("文件名:" + entry.getName()); // 正确输出中文
// 读取内容...
}
}
替代方案:手动修复 ZipEntry 名称(仅限已知编码)
若无法引入第三方库,且确认压缩包文件名是 GBK 或 UTF-8 编码,可尝试对 ZipEntry.getName() 返回的字符串重新解码:
- 先按平台默认编码(如 GBK)获取字节数组
- 再用目标编码(如 UTF-8)重建字符串
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
String rawName = entry.getName();
// 假设原始存为 UTF-8,但被误当 GBK 解析
byte[] bytes = rawName.getBytes(StandardCharsets.GBK);
String fixedName = new String(bytes, StandardCharsets.UTF_8);
System.out.println(fixedName);
}
注意:此法有风险,依赖压缩时的实际编码和当前 JVM 默认编码匹配,不推荐用于生产环境。
预防建议:生成 ZIP 时统一用 UTF-8 并开启 flag
如果你控制压缩过程,应确保生成 ZIP 时启用 UTF-8 编码标识:
- 使用
ZipOutputStream时,设置setUseUnicodeExtraFields(true) - 或选用 Commons Compress 的
ZipArchiveOutputStream,默认支持 UTF-8 和 flag 设置
zaos.setUseUnicodeExtraFields(true); // 关键:写入 UTF-8 flag
// 添加条目时 name 用 UTF-8 字符串即可
不复杂但容易忽略 —— 用对工具比硬调编码更可靠。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











