Java ZIP文件压缩和解压缩

513 阅读5分钟

Java ZIP文件压缩和解压缩

本案例实现文件的压缩和解压缩,共实现如下四个功能:

  1. 压缩多个文件至一个压缩文件中
  2. 压缩一个目录下的所有文件至一个压缩文件中(不保留目录结构)
  3. 压缩一个目录至一个压缩文件中(保留目录结构)
  4. 解压一个压缩文件

1. 功能一:压缩多个文件至一个压缩文件中

/**
 * 压缩多个文件,压缩后的所有文件在同一个目录
 *
 * @param zipFileName        压缩后的文件名, 准确来说是路径 + 文件名: C:/Users/xxx/Desktop/ + text.zip

 * @param files              需要压缩的文件
 * @param isContainDirectory 压缩文件中是否需要包含一个目录
 */
public static void zipMultipleFiles(String zipFileName, File[] files, boolean isContainDirectory) {
    // 输出流
    try (ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(zipFileName))) {
        if (isContainDirectory) {
            // 先添加一个 目录 ZipEntry
            zipOutputStream.putNextEntry(new ZipEntry(zipFileName + "/"));
            zipOutputStream.closeEntry();
            for (File file : files) {
                zipOutputStream.putNextEntry(new ZipEntry(zipFileName + "/" + file.getName()));
                readFileIOToZipOutputStream(file, zipOutputStream);
                zipOutputStream.closeEntry();
            }
        } else {
            // 遍历每一个文件,进行输出
            for (File file : files) {
                zipOutputStream.putNextEntry(new ZipEntry(file.getName()));
                readFileIOToZipOutputStream(file, zipOutputStream);
                zipOutputStream.closeEntry();
            }
        }
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

private static void readFileIOToZipOutputStream(File file, ZipOutputStream zipOutputStream) throws IOException {
    // 遍历每一个文件,进行输出
    FileInputStream fileInputStream = new FileInputStream(file);
    int len;
    byte[] bytes = new byte[1024];
    while ((len = fileInputStream.read(bytes)) != -1) {
        zipOutputStream.write(bytes, 0, len);
    }
    // 关闭流
    fileInputStream.close();
}

解释: ZipEntry这个类用于表示Zip文件条目,如图所示:

image.png

所以,压缩文件的思路如下:首先新建一个ZipOutputStream对象,然后将每个文件对象转化为ZipEntry对象,写入ZipOutputStream对象即可。

代码实战1: 压缩文件中不包含目录

package edu.tyut;

import edu.tyut.zip.ZipUtils;
import java.io.File;

public class Main {
    public static void main(String[] args) {
        File[] files = new File[]{new File("C:/Users/xxx/Desktop/TODO.txt"), new File("C:/Users/xxx/Desktop/text.txt")};
        ZipUtils.zipMultipleFiles("C:/Users/xxx/Desktop/text.zip", files, false);
    }
}

效果图如下:

image.png

代码实战2: 压缩文件中包含目录

package edu.tyut;

import edu.tyut.zip.ZipUtils;
import java.io.File;

public class Main {
    public static void main(String[] args) {
        File[] files = new File[]{new File("C:/Users/xxx/Desktop/TODO.txt"), new File("C:/Users/xxx/Desktop/text.txt")};
        ZipUtils.zipMultipleFiles("C:/Users/xxx/Desktop/text.zip", files, true);
    }
}

效果图如下:

image.png

点进text目录:

image.png

2. 功能二:压缩一个目录下的所有文件至一个压缩文件中(不保留目录结构)

/**
 * 压缩文件夹下的所有文件至同一个压缩文件,不保留目录结构
 *
 * @param zipFileName        被压缩的文件路径名称: 路径 + "/" + <b>名称<b/>
 * @param destDirectory      目标目录
 * @param isContainDirectory 压缩文件中是否需要包含一个目录
 */
public static void zipDirectoryFiles(String zipFileName, String destDirectory, boolean isContainDirectory) {
    ArrayList<File> fileList = new ArrayList<>();
    addFile(fileList, new File(destDirectory));
    ZipUtils.zipMultipleFiles(zipFileName, fileList.toArray(new File[]{}), isContainDirectory);
    System.out.println(Arrays.toString(fileList.toArray()));
}

private static void addFile(ArrayList<File> fileList, File file) {
    // 如果是文件的话
    if (file.isFile()) {
        fileList.add(file);
    } else {
        // 如果是目录的话
        File[] listFiles = file.listFiles();
        if (listFiles == null) return;
        for (File listFile : listFiles) {
            addFile(fileList, listFile);
        }
    }
}

注意: 有重名的文件的话就会出现异常,并且退出程序。

项目实战1:压缩一个SpringBoot快速启动模板项目(无重复文件名):


package edu.tyut;

import edu.tyut.zip.ZipUtils;

public class Main {
    public static void main(String[] args) {
        ZipUtils.zipDirectoryFiles("C:/Users/xxx/Desktop/template.zip", "C:/Users/xxx/Desktop/template", false);
    }
}

此处的isContainDirectory参数就不再演示了,同理。

效果图如下:

image.png

项目实战2:压缩一个SpringBoot快速启动模板项目(有重复文件名):

操作:将pom.xml文件复制到src目录中,继续执行上面的代码将报如下异常

Caused by: java.util.zip.ZipException: duplicate entry: pom.xml

3. 功能三:压缩一个目录至一个压缩文件中(保留目录结构)

代码如下所示:

/**
 * 压缩文件或者压缩目录,如果是压缩目录的话,保留目录结构
 *
 * @param zipFileName 压缩包文件名
 * @param zipFile     将被压缩的文件或者文件夹
 */
public static void zipFileTree(String zipFileName, File zipFile) {
    // 如果是文件的话,直接压缩
    if (zipFile.isFile()) {
        zipMultipleFiles(zipFileName, new File[]{zipFile}, true);
        return;
    }
    // 如果是目录的话、递归压缩
    try (ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(zipFileName))) {
        zip(zipFile, zipOutputStream, zipFile.getName());
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

/**
 * 递归压缩函数,保留目录结构树
 *
 * @param zipFile         待压缩的文件
 * @param zipOutputStream 压缩文件输出流
 * @param zipFileName     压缩后的文件名称
 */
private static void zip(File zipFile, ZipOutputStream zipOutputStream, String zipFileName) throws IOException {
    System.out.println(zipFileName);
    // 如果是目录的话
    if (zipFile.isDirectory()) {
        File[] listFiles = zipFile.listFiles();
        // 如果是空目录
        zipOutputStream.putNextEntry(new ZipEntry(zipFileName + "/"));
        // 如果不是空目录
        if (listFiles != null) {
            for (File listFile : listFiles) {
                zip(listFile, zipOutputStream, zipFileName + "/" + listFile.getName());
            }
        }
        zipOutputStream.closeEntry();
    } else {
        // 如果是文件的话
        // 向zip输出流中添加一个zip实体,构造器中name为zip实体的文件的名字
        zipOutputStream.putNextEntry(new ZipEntry(zipFileName));
        // copy 到文件输出流中
        FileInputStream fileInputStream = new FileInputStream(zipFile);
        byte[] bytes = new byte[1024];
        int len;
        while ((len = fileInputStream.read(bytes)) != -1) {
            zipOutputStream.write(bytes, 0, len);
        }
        fileInputStream.close();
        zipOutputStream.closeEntry();
    }
}

实战案例:压缩一个SpringBoot快速启动模板项目

package edu.tyut;

import edu.tyut.zip.ZipUtils;

import java.io.File;

public class Main {
    public static void main(String[] args) {
        ZipUtils.zipFileTree("C:/Users/xxx/Desktop/template.zip", new File("C:/Users/xxx/Desktop/template"));
    }
}

效果图如下:

image.png

压缩文件中保留了目录,点击目录进去:

image.png

4. 功能四:解压一个压缩文件

代码如下:

/**
 * @param zipFilePath   被解压文件的路径
 * @param destDirectory 解压文件存放目录 "src/main/resources"
 */
public static void unzip(String zipFilePath, String destDirectory) {
    File destDir = new File(destDirectory);
    // 如果目标目录不存在
    if (!destDir.exists()) {
        // 则创建
        boolean mkdirSuccess = destDir.mkdir();
        // 创建不成功,抛出异常,退出程序
        if (!mkdirSuccess) throw new RuntimeException("创建解压目标文件夹失败! \n" + destDirectory);
    }
    // 压缩文件读入流
    try (ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(zipFilePath))) {
        ZipEntry zipEntry = zipInputStream.getNextEntry();
        while (zipEntry != null) {
            String unzipFilePath = destDirectory + File.separator + zipEntry.getName();
            File unzipFile = new File(unzipFilePath);
            if (zipEntry.isDirectory()) {
                // 是目录的话就创建
                boolean isSuccess = unzipFile.mkdir();
                System.out.println("目录是否创建成功:" + isSuccess);
                if (!isSuccess) throw new RuntimeException("创建解压目标文件夹失败! \n" + unzipFilePath);
            } else {
                // 是文件的话,递归创建其父目录
                mkdir(unzipFile);
                BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(unzipFile));
                byte[] bytes = new byte[1024];
                int len;
                while ((len = zipInputStream.read(bytes)) != -1) {
                    bos.write(bytes, 0, len);
                }
                bos.close();
            }
            System.out.println(zipEntry);
            zipInputStream.closeEntry();
            zipEntry = zipInputStream.getNextEntry();
        }
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

// 递归创建目录
private static void mkdir(File file) {
    File parentFile = file.getParentFile();
    if (parentFile == null || parentFile.exists()) {
        return;
    }
    // 往下递
    mkdir(parentFile);
    boolean isSuccess = parentFile.mkdir();
    if (!isSuccess) throw new RuntimeException("创建解压父目录失败! ");
}

实战案例:解压上面的SpringBoot快速启动模板项目的压缩文件:

package edu.tyut;
import edu.tyut.zip.ZipUtils;
public class Main {
    public static void main(String[] args) {
        ZipUtils.unzip("C:/Users/xxx/Desktop/template.zip", "src/main/resources");
    }
}

OK,本篇文章,到这就结束了,用了简单的递归算法,原理很简单,如果有什么不足之处还请大佬批评指正,欢迎大家给我点个赞🎉。