Java zip文件名中文字符乱码问题解决方案

4,961 阅读2分钟

这是我参与8月更文挑战的第30天,活动详情查看:8月更文挑战

前言

常见的zip包工具类很多,由于代码中的场景对效率要求也不是很高, 其他几种工具这里不做讨论, 由于项目之前用的是zip4j, 这里讨论的zip解压中文字符问题,是对于zip4j的解决方案.

image.png

阿里Maven仓库
zip4j官网

<dependency>
    <groupId>net.lingala.zip4j</groupId>
    <artifactId>zip4j</artifactId>
    <version>2.6.4</version>
</dependency>

ZIP解压现状

ZIP格式在不同平台上使用不同软件压缩,大致结果为两类:

  1. Windows下使用WinRAR、好压等工具压缩的文件 特点:文件名为GBK编码
  2. 使用Linux、MacOS等系统压缩的zip文件 特点:文件名为UTF-8编码

GBK和UTF-8之间的区别参考文章

字符编码那点事:快速理解ASCII、Unicode、GBK和UTF-8

问题描述

代码之前在linux服务做的压缩, 然后上传到minio服务器上, 其他服务通过minio下载到本地解压, 文件包中文英文都没有问题. 新增场景本地可以传一些压缩包上传, 结果导致, 解压出来之后显示乱码, 代码异常, 本地写测试类模拟结果一样也是乱码.

问题代码

image.png

测试代码

image.png

结果

image.png

image.png

这种字符乱码问题的原因也很简单就是字符集的问题. 网上解决方案很多, 设置字符集为GBK, 使用ant-jar包解压等等. 没有统一的标准和方案,那么我们可以从API入手, 看看源码有没有预留字符集相关的设置, 找到问题的根本原因, 问题自然迎刃而解。

Zip4J源码分析

image.png 默认是UTF-8的形式

image.png

完整工具类

import cn.hutool.core.util.CharsetUtil;
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.FileHeader;
import org.apache.commons.collections.CollectionUtils;

import java.io.File;
import java.nio.charset.Charset;
import java.util.*;


public class MyZipUtils {

    /**
     * 解压zip包返回所有文件
     *
     * @param filePath 路径
     * @param unZipList 已解压列表
     * @return List<File>
     * @throws ZipException
     */
    public static List<File> scanAndUnzipFile(String filePath, Set<String> unZipList) throws ZipException {
        File file = new File(filePath);
        List<File> fileList = Arrays.asList(file.listFiles());
        List<File> fileListNew = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(fileList)) {
            for (File file1 : fileList) {
                if (file1.getName().indexOf(".zip") != -1) {
                    if (unZipList.contains(file1.getName())) {
                        continue;
                    }
                    unZipList.add(file1.getName());
                    ZipFile zipFile1 = new ZipFile(file1);
                    extractAll(filePath, zipFile1);
                    return scanAndUnzipFile(filePath, unZipList);
                }
                fileListNew.add(file1);
            }
            return fileListNew;
        }
        return null;
    }

public static String extractAll(String filePath, ZipFile zip) {
    zip.setCharset(Charset.forName("utf-8"));
    System.out.println("begin unpack zip file....");

    try {
        zip.getFileHeaders().forEach(v->{
            String extractedFile = getFileName(v);
            try {
                zip.extractFile(v, filePath ,extractedFile);
            } catch (ZipException e) {
                System.out.println("解压失败 :"+ extractedFile);
                e.printStackTrace();
                return;
            }
            System.out.println("解压成功 :"+extractedFile);
        });
    } catch (ZipException e) {
        e.printStackTrace();
    }
    System.out.println("unpack zip file success");
    return "success";
}

public static String getFileName(FileHeader fileHeader) {

    try {
        // 目前压缩包主要是两种来源WINdows和Linux
        if (fileHeader.isFileNameUTF8Encoded()) {
            return new String(fileHeader.getFileName().getBytes("Cp437"), CharsetUtil.CHARSET_UTF_8.name());
        } else {
            return new String(fileHeader.getFileName().getBytes("Cp437"), CharsetUtil.CHARSET_GBK.name());
        }
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
    return fileHeader.getFileName();
}


参考文档

unzip not correct with cjk filename. #45

Garbled chinese character #73