Java压缩和解压缩(三)Apache Commons Compress实践

Java压缩和解压缩
placeholder image
admin 发布于:2023-03-05 00:29:11
阅读:loading

1.基本介绍

Apache Commons Compress库定义了一系列操作文件压缩和解压缩的API,用于处理多种格式的文件(zip、gzip、tar、7z、jar等等),最新版本为1.2.2,JDK最低版本限制为1.8版本,这个项目组件中的代码有许多不同的起源,根据解压文件格式的不同区分,有不同的几个实现来源。对于最基本的zip格式文件提供的功能超越了java.util.zip中的实现。

本次研究Apache Commons Compress项目组件的实现主要是针对7z格式的压缩和解压缩,但是在进行源码分析实现和官网资料的了解后发现该项目支持读取7z格式的加密文件,但不支持写入加密文件,也就是说它的API不支持创建7z格式的加密实现,参考官网地址:“https://commons.apache.org”,源码地址:“https://gitbox.apache.org/repos/asf/commons-compress.git”。

2.代码实现

package cn.chendd.compress;

/**
 * 7z格式的压缩和解压缩
 *
 * @author chendd
 * @date 2023/3/5 12:36
 */
import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
import org.apache.commons.compress.archivers.sevenz.SevenZFile;
import org.apache.commons.compress.archivers.sevenz.SevenZMethod;
import org.apache.commons.compress.archivers.sevenz.SevenZOutputFile;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.MethodUtils;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 7z压缩和解压缩
 * 注意:Apache Commons Compress 官网说明不支持7z格式的生成带密码格式的压缩
 *
 * @author chendd
 * @date 2022/11/18 9:28
 */
public class Compress7z {

    /**
     * 创建压缩文件
     * @param inFile 源文件
     * @param outFile 7z格式
     * @throws IOException 异常处理
     */
    public static void sevenZ(File inFile, File outFile) throws IOException {
        try (SevenZOutputFile szos = new SevenZOutputFile(outFile)) {
            szos.setContentCompression(SevenZMethod.DEFLATE);
            generare7zFile(inFile, szos, inFile);
            szos.finish();
        }
    }

    /**
     * 解压缩文件
     * @param src7zFile 源文件
     * @param outFile 输出文件
     */
    public static void unSevenZ(File src7zFile, File outFile) {
        unSevenZ(src7zFile, outFile, null);
    }

    /**
     * 根据密码解压7z文件
     * @param src7zFile 源文件
     * @param outFile 输出文件
     * @param password 密码
     */
    public static void unSevenZ(File src7zFile, File outFile, String password) {
        if (src7zFile == null || outFile == null) {
            throw new NullPointerException("file can be not null");
        }
        SevenZFile sevenZFile = null;
        try {
            if (!src7zFile.exists()) {
                throw new FileNotFoundException(src7zFile.getAbsolutePath());
            }
            if (StringUtils.isBlank(password)) {
                sevenZFile = new SevenZFile(src7zFile);
            } else {
                sevenZFile = new SevenZFile(src7zFile, password.toCharArray());
            }
            byte[] bytes = new byte[IOUtils.DEFAULT_BUFFER_SIZE];
            SevenZArchiveEntry nextEntry = sevenZFile.getNextEntry();
            do {
                if (nextEntry == null) {
                    break;
                }
                if (nextEntry.isDirectory()) {
                    File file = new File(outFile, nextEntry.getName());
                    file.mkdirs();
                    continue;
                }
                File file = new File(outFile, nextEntry.getName());
                if (!file.getParentFile().exists()) {
                    file.getParentFile().mkdirs();
                }
                int lens;
                try (FileOutputStream outputStream = new FileOutputStream(file)) {
                    while ((lens = sevenZFile.read(bytes)) != -1) {
                        outputStream.write(bytes, 0, lens);
                    }
                }
            } while ((nextEntry = sevenZFile.getNextEntry()) != null);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            try {
                IOUtils.close(sevenZFile);
            } catch (IOException ignore) {
            }
        }
    }

    /**
     * 生成7z压缩文件
     * @param inFile 源文件
     * @param szos 输出流
     * @param rootFile 根文件
     * @throws IOException 异常处理
     */
    private static void generare7zFile(File inFile, SevenZOutputFile szos, File rootFile) throws IOException {
        if (inFile.isFile()) {
            putEntryFile(szos, rootFile, inFile);
            return;
        }
        File[] files = inFile.listFiles();
        if (files == null || files.length == 0) {
            putEntryEmptyFolder(szos, inFile, rootFile);
            return;
        }
        for (File file : files) {
            if (file.isDirectory()) {
                generare7zFile(file, szos, rootFile);
            } else {
                putEntryFile(szos, file, rootFile);
            }
        }
    }

    /**
     * 获取7z压缩包中的文件信息
     * 注意:
     * (1)未正常获取到压缩后的文件大小;
     * (2)与7z等软件的文件列表对比,当前的方法一次性罗列所有的文件夹以及子孙后代的所有文件;
     *
     * @param src7zFile 源7z文件
     * @param password  密码
     * @return 文件信息列表
     */
    public static List<View> view(File src7zFile, String password) {
        List<View> resultList = new ArrayList<>();
        try (SevenZFile sevenZFile = getSevenZFile(src7zFile, password)) {
            Iterable<SevenZArchiveEntry> entries = sevenZFile.getEntries();
            for (SevenZArchiveEntry entry : entries) {
                View view = new View();
                view.setDirectory(entry.isDirectory());
                view.setFileName(entry.getName());
                if (entry.getHasLastModifiedDate()) {
                    view.setLastModifiedTime(entry.getLastModifiedDate().getTime());
                } else if (entry.getHasCreationDate()) {
                    view.setLastModifiedTime(entry.getCreationDate().getTime());
                }
                view.setUncompressedSize(entry.getSize());
                long compressedSize = (long) MethodUtils.invokeMethod(entry, true, "getCompressedSize");
                view.setCompressedSize(compressedSize);
                resultList.add(view);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return resultList;
    }

    /**
     * 预览压缩文件列表
     * @param src7zFile 源文件
     * @return 文件列表
     */
    public static List<View> view(File src7zFile) {
        return view(src7zFile, null);
    }

    /**
     * 根据源文件和密码构造7z文件对象
     *
     * @param srcFile  源文件
     * @param password 密码
     * @return 7z文件对象
     * @throws IOException 异常处理
     */
    private static SevenZFile getSevenZFile(File srcFile, String password) throws IOException {
        if (StringUtils.isEmpty(password)) {
            return new SevenZFile(srcFile);
        }
        return new SevenZFile(srcFile, password.toCharArray());
    }

    /**
     * 压缩至文件
     * @param szos 输出流
     * @param file 文件
     * @param rootFile 根文件
     * @throws IOException 异常处理
     */
    private static void putEntryFile(SevenZOutputFile szos, File file, File rootFile) throws IOException {
        SevenZArchiveEntry archiveEntry = new SevenZArchiveEntry();
        boolean directory = file.isDirectory();
        String path = StringUtils.substringAfter(file.getAbsolutePath(), rootFile.getAbsolutePath() + File.separator);
        if (StringUtils.isBlank(path)) {
            archiveEntry.setName(file.getName());
        } else {
            archiveEntry.setName(path);
        }
        archiveEntry.setDirectory(directory);
        szos.putArchiveEntry(archiveEntry);
        try (InputStream inputStream = new FileInputStream(file)) {
            szos.write(inputStream);
        }
        szos.closeArchiveEntry();
    }

    /**
     * 压缩生成空文件夹
     * @param szos 输出流
     * @param file 文件
     * @param rootFile 根文件
     * @throws IOException 异常处理
     */
    private static void putEntryEmptyFolder(SevenZOutputFile szos, File file, File rootFile) throws IOException {
        SevenZArchiveEntry archiveEntry = new SevenZArchiveEntry();
        boolean directory = file.isDirectory();
        String path = StringUtils.substringAfter(file.getAbsolutePath(), rootFile.getAbsolutePath() + File.separator);
        if (StringUtils.isBlank(path)) {
            archiveEntry.setName(file.getName());
        } else {
            archiveEntry.setName(path);
        }
        archiveEntry.setDirectory(directory);
        szos.putArchiveEntry(archiveEntry);
        szos.closeArchiveEntry();
    }
}

3.测试代码

package cn.chendd.compress;

import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.junit.runners.MethodSorters;

import java.io.File;
import java.io.IOException;
import java.util.List;

/**
 * 7z压缩和解压缩示例
 *
 * @author chendd
 * @date 2023/3/5 17:25
 */
@RunWith(JUnit4.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class Compress7zTest {

    /**
     * 压缩文件
     */
    @Test
    public void sevenZipFile() throws IOException {
        File projectHome = new File(System.getProperty("user.dir")).getParentFile();
        File file = new File(projectHome , "源文件/哈喽.txt");
        File zipFile = new File(projectHome , "压缩文件夹/sevenZ_哈喽.7z");
        Compress7z.sevenZ(file , zipFile);
        System.out.println("压缩源文件:" + file.getAbsolutePath());
        System.out.println("压缩后文件:" + zipFile.getAbsolutePath());
    }

    /**
     * 压缩文件夹
     */
    @Test
    public void sevenZipFolder() throws IOException {
        File projectHome = new File(System.getProperty("user.dir")).getParentFile();
        File file = new File(projectHome , "源文件/复杂文件夹");
        File zipFile = new File(projectHome , "压缩文件夹/sevenZ_复杂文件夹.7z");
        Compress7z.sevenZ(file , zipFile);
        System.out.println("压缩源文件:" + file.getAbsolutePath());
        System.out.println("压缩后文件:" + zipFile.getAbsolutePath());
    }

    /**
     * 解压缩7z文件
     */
    @Test
    public void unSevenZipFile() {
        File projectHome = new File(System.getProperty("user.dir")).getParentFile();
        File zipFile = new File(projectHome , "压缩文件夹/sevenZ_哈喽.7z");
        File file = new File(projectHome , "解压缩文件夹/sevenZ_哈喽");
        Compress7z.unSevenZ(zipFile , file);
        System.out.println("压缩源文件:" + file.getAbsolutePath());
        System.out.println("压缩后文件:" + zipFile.getAbsolutePath());
    }

    /**
     * 解压缩7z文件夹
     */
    @Test
    public void unSevenZipFolder() {
        File projectHome = new File(System.getProperty("user.dir")).getParentFile();
        File zipFile = new File(projectHome , "压缩文件夹/sevenZ_复杂文件夹.7z");
        File file = new File(projectHome , "解压缩文件夹/sevenZ_复杂文件夹");
        Compress7z.unSevenZ(zipFile , file);
        System.out.println("压缩源文件:" + file.getAbsolutePath());
        System.out.println("压缩后文件:" + zipFile.getAbsolutePath());
    }

    /**
     * 解压缩带密码的7z文件【Apache Commons Compress不支持生成带密码的7z,需要自己手工创建】
     * 本例中的7z文件设置了压缩文件名
     */
    @Test
    public void unSevenZipFolderPassword() {
        File projectHome = new File(System.getProperty("user.dir")).getParentFile();
        File zipFile = new File(projectHome , "压缩文件夹/sevenZ_带密码_简单文件夹.7z");
        File file = new File(projectHome , "解压缩文件夹/sevenZ_带密码_简单文件夹");
        Compress7z.unSevenZ(zipFile , file , "https://www.chendd.cn");
        System.out.println("压缩源文件:" + file.getAbsolutePath());
        System.out.println("压缩后文件:" + zipFile.getAbsolutePath());
    }

    /**
     * 预览压缩包文件列表
     */
    @Test
    public void view() {
        File projectHome = new File(System.getProperty("user.dir")).getParentFile();
        File zipFile = new File(projectHome , "压缩文件夹/sevenZ_复杂文件夹.7z");
        System.out.println("预览压缩包的文件:");
        List<View> list = Compress7z.view(zipFile);
        list.forEach(System.out::println);
    }


}

4.运行示例

image.png

(项目示例结构)

image.png

(带密码的解压缩)

5.实现说明

(1)本文使用开源项目Apache Commons Compress 实现的“7z”格式文件(夹)的压缩和解压缩,仅以7z格式文件的操作进行示例实践;

(2)本文示例包含文件压缩、文件解压缩、文件夹压缩、文件夹解压缩、压缩包文件列表预览;

(4)本文示例支持压缩文件(夹)包含中文名称,支持带密码文件的解压缩,不支持生成带密码的7z压缩包

(5)项目源码下载地址:“https://gitee.com/88911006/chendd-examples/tree/master/compress”,详见commons-compress模块;

 点赞


 发表评论

当前回复:作者

 评论列表


留言区