由字符编码引起的zip解压错误
阅读 10
在Java中解压zip文件,需要留个心眼。
如果zip文件是在Linux系统中生成的,能正常解压。但如果是在Windows系统中生成的,解压时可能会报错:
java.util.zip.ZipException: invalid CEN header (bad entry name or comment)这是为啥呢?
原因是 ZIP 文件内文件名的编码与 Java 解析时使用的编码不一致,导致字节序列无法被正确解码。常见的中文环境 ZIP 文件通常使用 GBK,而国际标准或 Linux/macOS 生成的通常使用 UTF-8。
所以,我们可以写一个解压的通用方法:
private static void doExtract(File zipFile, Path targetDir, Charset charset) throws IOException {
try (var zf = new ZipFile(zipFile, charset)) {
var entries = zf.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
// 只取文件名(去掉目录)
var rawName = entry.getName();
Path normalized = Path.of(rawName).normalize();
String safePDFName = normalized.getFileName().toString().trim();
Path targetFile = targetDir.resolve(safePDFName);
// 流式写入磁盘 (防止OOM)
try (var is = new BufferedInputStream(zf.getInputStream(entry));
var fos = Files.newOutputStream(targetFile)) {
byte[] buffer = new byte[8192];
int len;
while ((len = is.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
}
}
}
}调用方式如下:
public void unZip() throws IOException {
String home = System.getProperty("user.home");
Path zipPath = Path.of(home, "w10.zip");
File zipFile = zipPath.toFile();
Path targetDir = Path.of(home, "new-download");
if (!Files.exists(targetDir)) {
Files.createDirectories(targetDir);
}
try {
// 优先尝试标准 UTF-8 (MacOS 和 Linux)
doExtract(zipFile, targetDir, StandardCharsets.UTF_8);
} catch (ZipException e) {
// 如果报编码或头部错误,很大可能是 Windows 的 GBK 压缩包,切换到 GBK
log.warn("解压失败,尝试使用 GBK 编码重试...", e);
doExtract(zipFile, targetDir, Charset.forName("GBK"));
}
}其中,关键是在new ZipFile(zipFile, charset)的第二个参数中,手动指定正确的字符编码。
最后编辑于: 2026-09-16