由字符编码引起的zip解压错误

阅读 9
标签: Java

在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

评论(0条)

(必填)
复制成功