JAVA实现文件压缩和解压,解决Java原生压缩组件不支持中文文件名的问题


前言

最近由于要和联通移动做接口对接,需求要求将文件压缩成ZIP格式,然后上传到移动和联通。最开始想到使用JDK原生API提供的ZipOutputStream做文件压缩,使用ZipFile将ZIP文件解压。网上提供了很多JAVA实现ZIP文件压缩和解压的案列,大多数都是采用JAVA原生API实现,但是当压缩和解压文件为中文名称时,原生API会出现中文文件压缩和解压失败的情况。而且网上提供的案例几乎都避开了压缩和解压中文文件这个坑,于是我开始找寻新的方案来解决JAVA压缩和解压中文文件。


一、JAVA原生ZipOutputStream无法压缩中文文件夹

在测试使用JAVA原生ZipOutputStream压缩文件时,对所有的英文文件压缩都没问题,但是当改为中文文件时,发现压缩的中文文件夹压缩失败了。这里我在initDatla文件下建了几个文件夹,其中包括一个中文的空文件夹叫空文件,当采用ZipOutPutStream压缩后,解压查看发现这个空文件压缩失败了。
在这里插入图片描述
压缩initData文件后得到的压缩文件ZipFile.zip。
在这里插入图片描述
解压zipFile.zip文件后,发现这个有中文文件夹压缩失败。
在这里插入图片描述

二、为什么JAVA原生API无法压缩中文文件夹

JAVA原生的ZIpOutputStream默认采用的编码是UTF-8,但是中文文件夹默认采用的编码是GBK,这导致了在压缩中文文件时会出现压缩失败或者乱码的情况。在ZipOutputStream的构造函数中已经写死了字符编码为UTF-8,这导致中文件夹压缩失败。
在这里插入图片描述
查看ZipOutputStream源码,发现并没有提供重置编码的方法,那就是说无法更改ZIpOutputStream的编码类型,这也是JDK原生API不支持中文文件夹压缩解压的原因。经过寻找发现,Apache的ant包中的压缩组件修复了这个问题,如果你在使用压缩功能时需要支持中文文件名,建议你直接使用Apache的压缩组件来实现这个功能。

三、采用ANT包实现文件压缩和解压(支持中文文件)

Apache的ant包中提供的ZipOutputStream解决了压缩中文文件失败和乱码的问题,在ZipOutPutStream中提供了setEncoding()方法,可以让我们设置编码类型。

(1)引入POM依赖

        <dependency>
            <groupId>org.apache.ant</groupId>
            <artifactId>ant</artifactId>
            <version>1.9.3</version>
        </dependency>

使用ant包中的ZipOutPutStream类通过setEncoding("GBK")方法和ZipFile构造函数ZipFile(File f, String encoding)可以完美的解决中文文件问题。以下代码是经过本人多次测试写的工具类,可以直接运行并且有效。如有需要请直接复制代码即可。

/**
 * @Author: Greyfus
 * @Create: 2022-07-06 10:43
 * @Version: 1.0.0
 * @Description:compress the file into ZIP format,Unzip ZIP format
 * PS:JDK原生的ZipOutInputStream默认编码为UTF8,而中文默认编码为GBK,如果对中文文件压缩可能出现压缩失败的情况,
 * 采用org.apache.ant封装的ZipOutInputStream,通过setEncoding()方法可以解决这个问提。
 * JDK原生的ZipFile默认编码为UTF8,使用org.apache.ant封装的ZipFile,通过new ZipFile(srcZipFile, "GBK")构造函数
 * 可以解决中文文件解压后乱码的问题。
 */
package com.seeker.gzip;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.Expand;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;


public class ZIPFileUtils {
    
    


    private static final Logger LOGGER = LoggerFactory.getLogger(ZIPFileUtils.class);

    private ZIPFileUtils() {
    
    

    }

    /**
     * @param srcDir            源文件路径
     * @param targetDir         压缩文件路径
     * @param maintainStructure 压缩时是否按照源文件结构模式压缩
     */
    public static void zipFile(String srcDir, String targetDir, boolean maintainStructure) {
    
    
        checkParameters(srcDir, targetDir);
        zipFile(new File(srcDir), new File(targetDir), maintainStructure);
    }

    /**
     * @param srcDir            源文件
     * @param targetDir         压缩文件
     * @param maintainStructure 压缩时是否按照源文件结构模式压缩
     */
    public static void zipFile(File srcDir, File targetDir, boolean maintainStructure) {
    
    
        try {
    
    
            zipFile(srcDir, new BufferedOutputStream(new FileOutputStream(targetDir)), maintainStructure);
        } catch (FileNotFoundException e) {
    
    
            e.printStackTrace();
        }
    }

    public static void zipFile(File srcDir, OutputStream out, boolean maintainStructure) {
    
    

        if (!srcDir.exists()) {
    
    
            throw new RuntimeException(ZIPFileEnum.SOURCE_FILE_NOT_EXISTS.getMessage());
        }

        try (ZipOutputStream zos = new ZipOutputStream(out)) {
    
    
            /**
             * JDK原生的ZipOutputStream默认的是编码是UTF,然而中文编码是GBK,导致原生的JDK提供的API有时候无法压缩中文文件夹
             *
             */
            long startTime = System.currentTimeMillis();
            zos.setEncoding("GBK");
            compress(srcDir, zos, srcDir.getName(), maintainStructure);
            LOGGER.info("ZIP files spend [{}] millisecond", System.currentTimeMillis() - startTime);
        } catch (Exception e) {
    
    
            throw new RuntimeException(e);
        }
    }

    /**
     * 压缩文件具体流程
     *
     * @param srcDir
     * @param zos
     * @param entryName         记录每个文件的路径
     * @param maintainStructure
     */
    private static void compress(File srcDir, ZipOutputStream zos, String entryName, boolean maintainStructure) {
    
    

        if (srcDir.isFile()) {
    
    

            try (FileInputStream in = new FileInputStream(srcDir)) {
    
    

                zos.putNextEntry(new ZipEntry(entryName));
                int len;
                byte[] bytes = new byte[1024];
                while ((len = in.read(bytes)) != -1) {
    
    
                    zos.write(bytes, 0, len);
                }
                zos.closeEntry();

            } catch (IOException e) {
    
    
                throw new RuntimeException(e);
            }
        }

        if (srcDir.isDirectory()) {
    
    

            File[] listFiles = srcDir.listFiles();
            if (ArrayUtils.isEmpty(listFiles)) {
    
    
                /**
                 * 处理空文件夹压缩问题,JDK原生API无法处理中文件夹压缩
                 */
                try {
    
    
                    zos.putNextEntry(new ZipEntry(entryName + "/"));
                    zos.closeEntry();
                } catch (IOException e) {
    
    
                    throw new RuntimeException(e);
                }
            }

            for (File srcFile : listFiles) {
    
    
                if (maintainStructure) {
    
    
                    compress(srcFile, zos, entryName + File.separator + srcFile.getName(), maintainStructure);
                } else {
    
    
                    compress(srcFile, zos, srcFile.getName(), maintainStructure);
                }
            }
        }
    }

    /**
     * 解压文件
     *
     * @param srcZipFile zip文件路径
     * @param targetFile 解压后的存储地址
     */
    public static void unzipFile(String srcZipFile, String targetFile) {
    
    
        checkParameters(srcZipFile, targetFile);
        unzipFile(new File(srcZipFile), targetFile);
    }

    /**
     * 解压文件
     *
     * @param srcZipFile zip文件
     * @param targetDir  解压后的存储地址
     */
    public static void unzipFile(File srcZipFile, String targetDir) {
    
    
        if (!srcZipFile.exists()) {
    
    
            throw new RuntimeException(ZIPFileEnum.SOURCE_FILE_NOT_EXISTS.getMessage());
        }
        Project project = new Project();
        Expand expand = new Expand();
        expand.setProject(project);
        expand.setSrc(srcZipFile);
        expand.setOverwrite(true);
        File file = new File(targetDir);
        if (!file.exists()) {
    
    
            file.mkdirs();
        }
        expand.setDest(file);
        expand.execute();
    }


    private static void checkParameters(String srcDir, String targetDir) {
    
    
        if (StringUtils.isEmpty(srcDir)) {
    
    
            throw new RuntimeException(ZIPFileEnum.SOURCE_FILE_IS_EMPTY.getMessage());
        }
        if (StringUtils.isEmpty(targetDir)) {
    
    
            throw new RuntimeException(ZIPFileEnum.TARGET_FILE_IS_EMPTY.getMessage());
        }
        if (!new File(srcDir).exists()) {
    
    
            throw new RuntimeException(ZIPFileEnum.SOURCE_FILE_NOT_EXISTS.getMessage());
        }
    }

    private enum ZIPFileEnum {
    
    

        SOURCE_FILE_IS_EMPTY("0001", "Source file is empty!"),
        TARGET_FILE_IS_EMPTY("0002", "Target file is Empty !"),
        SOURCE_FILE_NOT_EXISTS("0003", "Source file not exists");

        private String code;
        private String message;

        ZIPFileEnum(String code, String message) {
    
    
            this.code = code;
            this.message = message;
        }

        public String getCode() {
    
    
            return code;
        }

        public String getMessage() {
    
    
            return message;
        }
    }
}



(2)测试压缩和解压中文文件效果

ZIPFileUtils工具类有只有两个方法,一个是zipFile方法用于压缩文件,一个是unzipFile方法用于解压文件。
在这里插入图片描述
zipFile方法要多个重载方法,可以接收不同的参数,更多的参数使用请查看源码。
在这里插入图片描述

这里做一个演示,将E:/app/initData目录的文件压缩,压缩后的文件路径为E:/app/zipFile.zip,并且保持源文件目录结构。
在这里插入图片描述
压缩完成后查看压缩文件是否生成,并解压zipFile.zip文件,查看中文文件是否压缩成功。
在这里插入图片描述
解压zipFile.zip查看压缩效果。
在这里插入图片描述
对于unzipFile方法解压文件就不在叙述了,因为比较简单。如果有任何疑问或者意见可以留言。

猜你喜欢

转载自blog.csdn.net/qq_43600166/article/details/125649517
今日推荐