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() 返回的字符串重新解码:
将后端 Java 接口文档转换为前端 TypeScript 类型定义(interface / type)。输入:飞书文档、Spring Boot 接口文档、Swagger/OpenAPI、手写 API 文档等。输出:包含请求参数与响应结果的 .md 文件,含完整 JSDoc 注释。触发关键词:Java 转 TS、接口转换、生成前端类型、接口文档转 TypeScript、DTO/VO/BO 转 interface、后端文档转前端类型、生成接口类型、类型定义生成。
立即学习“Java免费学习笔记(深入)”;
- 先按平台默认编码(如 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 字符串即可
不复杂但容易忽略 —— 用对工具比硬调编码更可靠。

















