Java ZIP文件压缩和解压缩
本案例实现文件的压缩和解压缩,共实现如下四个功能:
- 压缩多个文件至一个压缩文件中
- 压缩一个目录下的所有文件至一个压缩文件中(不保留目录结构)
- 压缩一个目录至一个压缩文件中(保留目录结构)
- 解压一个压缩文件
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文件条目,如图所示:
所以,压缩文件的思路如下:首先新建一个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);
}
}
效果图如下:
代码实战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);
}
}
效果图如下:
点进text
目录:
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
参数就不再演示了,同理。
效果图如下:
项目实战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"));
}
}
效果图如下:
压缩文件中保留了目录,点击目录进去:
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,本篇文章,到这就结束了,用了简单的递归算法,原理很简单,如果有什么不足之处还请大佬批评指正,欢迎大家给我点个赞🎉。