压缩文件或文件夹工具类

最近有个需求是要压缩指定目录下所有的文件及文价夹。看了网上一些实现,明白了大概流程,于是为了契合自己的业务需求,自己封装了个工具类。

支持功能

  1. 给定一个文件夹或文件的路径,输出的压缩包路径,生成压缩包。
  2. 给定一个路径集合,将不同路径的文件或文件夹下的包压缩在一个包下。
  3. 可以自行决定是否添加所有文件的上级文件夹名称。

实现代码

/**
 * @Auther: binghua.zheng
 * @Date: 2021/9/5 14:14
 * @Description:
 */
public class FileCompressUtil {

    private static final Logger log = LoggerFactory.getLogger(FileCompressUtil.class);

    /**
     * 文件夹层级连接符
     */
    private static final String FOLDER_LEVEL = "/";

    /**
     *  ""
     */
    private static final String EMPTY_STR = "";

    /**
     * 压缩包根路径
     */
    private static final ThreadLocal<String> PARENT_PATH = ThreadLocal.withInitial(() -> EMPTY_STR);

    /**
     * 压缩包根路径源
     */
    private static final ThreadLocal<String> PARENT_SOURCE_PATH = ThreadLocal.withInitial(() -> EMPTY_STR);

    /**
     * 压缩包基础路径
     */
    private static final ThreadLocal<String> BASE_PATH = ThreadLocal.withInitial(() -> EMPTY_STR);

    /**
     * 压缩文件
     *
     * @param fileUrl 准备压缩的文件或文件夹
     * @param outZipUrl 要输出的压缩包路径
     * @param retainFolder 如果是文件夹,是否要保留当前文件层级
     */
    public static void compress(String fileUrl, String outZipUrl, boolean retainFolder) {
        compress(fileUrl, outZipUrl, EMPTY_STR, retainFolder);
    }

    /**
     * 压缩文件
     *
     * @param fileUrl 准备压缩的文件或文件夹
     * @param outZipUrl 要输出的压缩包路径
     * @param parentPath 要输出的压缩包父路径
     * @param retainFolder 如果是文件夹,是否要保留当前文件层级
     */
    public static void compress(String fileUrl, String outZipUrl, String parentPath, boolean retainFolder) {
        try (
            // 创建ZIP输出流
            OutputStream os = new FileOutputStream(outZipUrl);
            BufferedOutputStream bos = new BufferedOutputStream(os);
            ZipOutputStream zos = new ZipOutputStream(bos)
        ){
            File file = new File(fileUrl);
            if (!checkFile(file)) {
                log.error("找不到文件,无法压缩");
                return ;
            }
            // 设定zip根文件夹
            if (StringUtils.hasText(parentPath)) {
                PARENT_PATH.set(parentPath + FOLDER_LEVEL);
            }
            // 如果有设置父路径,提前将父路径放入zip中c
            doGenerateParentPath(zos);
            // 以当前文件夹为起点来压缩
            if (retainFolder && file.isDirectory()) {
                createZipEntryBaseDirectory(file, zos);
            }
            // 配置基础路径
            configBasePath(file);
            // 开始压缩
            compressFile(file, zos);
            zos.closeEntry();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("****** 压缩文件失败 ******");
        } finally {
            // 清除缓存
            PARENT_PATH.remove();
            BASE_PATH.remove();
            PARENT_SOURCE_PATH.remove();
        }
    }

    /**
     * 将指定路径的文件聚合在一起压缩
     *
     * @param filePaths 文件路径
     * @param outZipUrl 要输出的压缩包路径
     */
    public static void compress(List<String> filePaths, String outZipUrl) {
        compress(filePaths, outZipUrl, EMPTY_STR);
    }

    /**
     * 将指定路径的文件聚合在一起压缩
     *
     * @param filePaths 文件路径
     * @param outZipUrl 要输出的压缩包路径
     * @param parentPath 要输出的压缩包父路径
     */
    public static void compress(List<String> filePaths, String outZipUrl, String parentPath) {
        try (
            // 创建ZIP输出流
            OutputStream os = new FileOutputStream(outZipUrl);
            BufferedOutputStream bos = new BufferedOutputStream(os);
            ZipOutputStream zos = new ZipOutputStream(bos)
        ){
            // 设定zip根文件夹
            if (StringUtils.hasText(parentPath)) {
                PARENT_PATH.set(parentPath + FOLDER_LEVEL);
                PARENT_SOURCE_PATH.set(parentPath + FOLDER_LEVEL);
            }
            // 如果有设置父路径,提前将父路径放入zip中
            doGenerateParentPath(zos);
            // 循环创建压缩包
            for (String filePath : filePaths) {
                doProcessCompress(filePath, zos);
                // 恢复原路径
                PARENT_PATH.set(PARENT_SOURCE_PATH.get());
            }
            zos.closeEntry();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("****** 压缩文件失败 ******");
        } finally {
            // 清除缓存
            PARENT_PATH.remove();
            BASE_PATH.remove();
            PARENT_SOURCE_PATH.remove();
        }
    }

    /**
     * 执行压缩流程
     *
     * @param fileUrl 文件路径
     * @param zos ZIP输出流
     */
    private static void doProcessCompress(String fileUrl, ZipOutputStream zos) throws IOException {
        File file = new File(fileUrl);
        if (!checkFile(file)) {
            log.error("找不到文件,无法压缩");
            return ;
        }
        // 基础路径
        configBasePath(file);
        // 如果是文件夹,则创建文件夹层级
        createZipEntryBaseDirectory(file, zos);
        // 开始压缩
        compressFile(file, zos);
    }

    /**
     * 校验文件是否存在
     *
     * @param file 文件
     * @return true -> 存在; false -> 不存在
     */
    private static boolean checkFile(File file) {
        return file.exists();
    }

    /**
     * 以文件夹为名,创建ZipEntry
     *
     * @param file 文件夹
     * @param zos zip输出流
     */
    private static void createZipEntryBaseDirectory(File file, ZipOutputStream zos) throws IOException {
        if (file.isDirectory()) {
            String zipPath = PARENT_PATH.get() + file.getName() + FOLDER_LEVEL;
            doCreateZipEntry(zipPath, zos);
            PARENT_PATH.set(zipPath);
        }
    }

    /**
     * 配置基础路径
     *
     * @param file 文件
     */
    private static void configBasePath(File file) {
        if (file.isFile()) {
            BASE_PATH.set(file.getParent());
        } else {
            BASE_PATH.set(file.getPath());
        }
    }

    /**
     * 指定根文件,生成其内部所有文件的压缩包
     *
     * @param file 文件
     * @param zos zip输出流
     */
    private static void compressFile(File file, ZipOutputStream zos) throws IOException {
        File[] fileList = file.isDirectory() ? file.listFiles() : new File[]{file};
        if (fileList == null || fileList.length == 0) {
            log.info("****** 找不到文件,过滤此文件压缩 ******");
            return;
        }
        // 解析压缩所有子文件夹
        for (File e : fileList) {
            doCompressFile(e, zos);
        }
    }

    /**
     * 压缩文件
     *
     * @param file 文件
     * @param zos zip输出流
     */
    private static void doCompressFile(File file, ZipOutputStream zos) throws IOException {
        // 以当前层级目录添加Zip文件入口
        createZipEntry(file, zos);
        // 写入输出流
        if (file.isFile()) {
            doWriteZipStream(file, zos);
        } else {
            // 继续解析子文件层级
            compressFile(file, zos);
        }
    }

    /**
     * 根据File创建ZipEntry
     *
     * @param file 文件
     * @param zos zip输出流
     */
    private static void createZipEntry(File file, ZipOutputStream zos) throws IOException {
        doCreateZipEntry(doGenerateZipEntryName(file), zos);
    }

    /**
     * 根据文件或文件夹,生成压缩包结构路径
     *
     * @param file 文件
     * @return 压缩包相对路径
     */
    private static String doGenerateZipEntryName(File file) {
        return PARENT_PATH.get() + file.getPath().substring(BASE_PATH.get().length() + 1) + (file.isDirectory() ? FOLDER_LEVEL : EMPTY_STR);
    }

    /**
     * 设置父路径
     *
     * @param zos ZIP输出流
     */
    private static void doGenerateParentPath(ZipOutputStream zos) throws IOException {
        // 如果有设置父路径,提前将父路径放入zip中
        if (StringUtils.hasText(PARENT_PATH.get())) {
            doCreateZipEntry(PARENT_PATH.get(), zos);
        }
    }

    /**
     * 创建压缩包内的文件夹及文件层级
     *
     *
     * @param pathName 相对压缩包路径的各文件路径
     * @param zos zip输出流
     */
    private static void doCreateZipEntry(String pathName, ZipOutputStream zos) throws IOException {
        zos.putNextEntry(new ZipEntry(pathName));
    }

    /**
     * 将文件写入ZIP输出流
     *
     * @param file 要压缩的文件
     * @param zos ZIP输出流
     */
    private static void doWriteZipStream(File file, ZipOutputStream zos) throws IOException {
        InputStream is = new FileInputStream(file);
        BufferedInputStream bis = new BufferedInputStream(is);
        byte[] buf = new byte[1024];
        int length;
        while ((length = bis.read(buf)) > 0) {
            zos.write(buf, 0, length);
        }
        bis.close();
        is.close();
    }


}

测试

指定目录

在这里插入图片描述
我要压缩文件夹1下所有的文件夹及文件。

	@Test
    public void test1() throws Exception {
        String orderId = "123456";
        String taskId = "123";
        String reportId = "1";
        String parentReportDate = "202109";
        String reportDate = "20210905";

        // 收集所有报告,准备打包
        String url = REPORT_ROOT_PATH_PREFIX + LEVEL + parentReportDate + LEVEL + reportDate + LEVEL + orderId + CONNECT + taskId + LEVEL + reportId;
        String outZipPath = url + ".zip";
//        List<String> pathList = new ArrayList<>();
//        pathList.add("G:\\防护报告\\202109\\20210905\\Spring Security 实战干货.pdf");
//        pathList.add("G:\\防护报告\\202109\\20210905\\123456_123\\1\\pdf003");
//        pathList.add("G:\\防护报告\\202109\\20210905\\123456_123\\1\\报告001");
//        pathList.add("G:\\防护报告\\202109\\20210905\\123456_123\\1\\回执单");

        // compress
        FileCompressUtil.compress(url, outZipPath, "测试文件顶层目录", false);
    }

在这里插入图片描述
解压后效果
在这里插入图片描述

指定多个路径

    @Test
    public void test1() throws Exception {
        String orderId = "123456";
        String taskId = "123";
        String reportId = "1";
        String parentReportDate = "202109";
        String reportDate = "20210905";

        // 收集所有报告,准备打包
        String url = REPORT_ROOT_PATH_PREFIX + LEVEL + parentReportDate + LEVEL + reportDate + LEVEL + orderId + CONNECT + taskId + LEVEL + reportId;
        String outZipPath = url + ".zip";
        List<String> pathList = new ArrayList<>();
        pathList.add("G:\\防护报告\\202109\\20210905\\Spring Security 实战干货.pdf");
        pathList.add("G:\\防护报告\\202109\\20210905\\123456_123\\1\\pdf003");
        pathList.add("G:\\防护报告\\202109\\20210905\\123456_123\\1\\报告001");
        pathList.add("G:\\防护报告\\202109\\20210905\\123456_123\\1\\回执单");

        // compress
        FileCompressUtil.compress(pathList, outZipPath);
    }

效果如下
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值