zip压缩
Java 压缩与解压缩操作详解:ZIP 与 GZIP 实践
在 Java 中,处理压缩文件是常见需求,尤其是在文件传输、存储优化等场景。JDK 内置了对 ZIP 和 GZIP 格式的支持,通过 java.util.zip 包中的类可实现压缩和解压缩操作。本文将详细介绍 ZIP 和 GZIP 的使用方法,包括单文件 / 多文件压缩、文件夹递归压缩及解压缩的具体实现。
ZIP 压缩与解压缩
ZIP 是一种广泛使用的归档格式,支持将多个文件或文件夹压缩到一个 .zip 文件中,且可保留目录结构。Java 中通过 ZipInputStream(解压缩)和 ZipOutputStream(压缩)实现 ZIP 操作。
ZIP 解压缩(ZipInputStream)
ZipInputStream 用于读取 ZIP 文件中的条目(文件或文件夹),通过 getNextEntry() 遍历所有条目,再通过输入流读取具体内容。
核心步骤:
- 创建
ZipInputStream关联 ZIP 文件输入流。 - 循环调用
getNextEntry()获取每个ZipEntry(条目)。 - 对每个条目,通过
ZipInputStream读取数据(若为文件夹则跳过内容读取)。 - 处理完成后调用
closeEntry()关闭当前条目,最终关闭流。
示例:解压缩 ZIP 文件到指定目录
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class ZipUncompressor {
/**
* 解压缩 ZIP 文件到目标目录
* @param zipFilePath ZIP 文件路径
* @param destDir 目标目录
*/
public static void unzip(String zipFilePath, String destDir) throws IOException {
// 创建目标目录(不存在则创建)
File destDirFile = new File(destDir);
if (!destDirFile.exists()) {
destDirFile.mkdirs();
}
try (ZipInputStream zis = new ZipInputStream(
new BufferedInputStream(new FileInputStream(zipFilePath)),
StandardCharsets.UTF_8)) { // 指定编码(解决中文乱码)
ZipEntry entry;
// 遍历所有条目
while ((entry = zis.getNextEntry()) != null) {
String entryName = entry.getName();
File entryFile = new File(destDirFile, entryName);
if (entry.isDirectory()) {
// 若为文件夹,创建目录
entryFile.mkdirs();
} else {
// 若为文件,读取内容并写入目标文件
// 确保父目录存在
File parentDir = entryFile.getParentFile();
if (!parentDir.exists()) {
parentDir.mkdirs();
}
try (BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(entryFile))) {
byte[] buffer = new byte[1024];
int len;
while ((len = zis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
}
}
zis.closeEntry(); // 关闭当前条目
}
}
}
public static void main(String[] args) throws IOException {
unzip("test.zip", "unzip_result");
System.out.println("解压缩完成!");
}
}
注意事项:
- 中文乱码:
ZipInputStream构造器需指定编码(如StandardCharsets.UTF_8),避免条目名称中文乱码。 - 目录处理:通过
entry.isDirectory()判断是否为文件夹,需创建对应目录结构。
ZIP 压缩(ZipOutputStream)
ZipOutputStream 用于将文件或文件夹压缩为 ZIP 文件,通过 putNextEntry() 写入条目信息,再写入具体内容。
核心步骤:
- 创建
ZipOutputStream关联目标 ZIP 文件输出流。 - 对每个需要压缩的文件 / 文件夹,创建
ZipEntry并调用putNextEntry()。 - 写入文件内容(文件夹无需写入内容,仅创建条目)。
- 调用
closeEntry()关闭当前条目,最终关闭流。
示例:压缩文件或文件夹为 ZIP
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class ZipCompressor {
/**
* 压缩文件或文件夹为 ZIP
* @param sourcePath 源文件/文件夹路径
* @param zipFilePath 目标 ZIP 文件路径
*/
public static void zip(String sourcePath, String zipFilePath) throws IOException {
File sourceFile = new File(sourcePath);
if (!sourceFile.exists()) {
throw new FileNotFoundException("源文件/文件夹不存在:" + sourcePath);
}
try (ZipOutputStream zos = new ZipOutputStream(
new BufferedOutputStream(new FileOutputStream(zipFilePath)),
StandardCharsets.UTF_8)) { // 指定编码
// 递归压缩(根据源是否为文件夹处理)
if (sourceFile.isDirectory()) {
// 压缩文件夹(传入父路径为空,避免顶级目录冗余)
compressDirectory(zos, sourceFile, "");
} else {
// 压缩单个文件
compressFile(zos, sourceFile, "");
}
}
}
/**
* 递归压缩文件夹
* @param zos ZIP 输出流
* @param dir 待压缩文件夹
* @param parentEntryName 父条目名称(用于构建条目路径)
*/
private static void compressDirectory(ZipOutputStream zos, File dir, String parentEntryName) throws IOException {
File[] files = dir.listFiles();
if (files == null) {
return; // 空文件夹
}
// 构建当前文件夹的条目名称(末尾加斜杠表示文件夹)
String dirEntryName = parentEntryName + dir.getName() + File.separator;
zos.putNextEntry(new ZipEntry(dirEntryName));
zos.closeEntry(); // 文件夹条目无需内容,直接关闭
// 递归处理子文件/子文件夹
for (File file : files) {
if (file.isDirectory()) {
compressDirectory(zos, file, dirEntryName);
} else {
compressFile(zos, file, dirEntryName);
}
}
}
/**
* 压缩单个文件
* @param zos ZIP 输出流
* @param file 待压缩文件
* @param parentEntryName 父条目名称(用于构建条目路径)
*/
private static void compressFile(ZipOutputStream zos, File file, String parentEntryName) throws IOException {
String entryName = parentEntryName + file.getName();
zos.putNextEntry(new ZipEntry(entryName)); // 写入条目信息
// 写入文件内容
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) {
byte[] buffer = new byte[1024];
int len;
while ((len = bis.read(buffer)) != -1) {
zos.write(buffer, 0, len);
}
}
zos.closeEntry(); // 关闭当前条目
}
public static void main(String[] args) throws IOException {
// 压缩单个文件
zip("test.txt", "single_file.zip");
// 压缩文件夹
zip("docs", "docs.zip");
System.out.println("压缩完成!");
}
}
注意事项:
- 条目路径:通过
parentEntryName维护目录结构,确保解压后目录层级正确。 - 文件夹标识:文件夹条目名称需以
File.separator(如/或\)结尾,否则解压时可能被识别为文件。
GZIP 压缩与解压缩
GZIP 是一种用于压缩单个文件的格式(不支持多文件或文件夹),压缩后文件通常以 .gz 为扩展名。Java 中通过 GZIPInputStream(解压缩)和 GZIPOutputStream(压缩)实现。
GZIP 压缩(GZIPOutputStream)
示例:压缩单个文件为 GZIP
import java.io.*;
import java.util.zip.GZIPOutputStream;
public class GzipCompressor {
/**
* 压缩单个文件为 GZIP
* @param sourceFilePath 源文件路径
* @param gzipFilePath 目标 GZIP 文件路径
*/
public static void gzip(String sourceFilePath, String gzipFilePath) throws IOException {
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourceFilePath));
GZIPOutputStream gzos = new GZIPOutputStream(
new BufferedOutputStream(new FileOutputStream(gzipFilePath)))) {
byte[] buffer = new byte[1024];
int len;
while ((len = bis.read(buffer)) != -1) {
gzos.write(buffer, 0, len);
}
}
}
public static void main(String[] args) throws IOException {
gzip("data.txt", "data.txt.gz");
System.out.println("GZIP 压缩完成!");
}
}
GZIP 解压缩(GZIPInputStream)
示例:解压缩 GZIP 文件
import java.io.*;
import java.util.zip.GZIPInputStream;
public class GzipUncompressor {
/**
* 解压缩 GZIP 文件
* @param gzipFilePath GZIP 文件路径
* @param destFilePath 目标文件路径
*/
public static void ungzip(String gzipFilePath, String destFilePath) throws IOException {
try (GZIPInputStream gzis = new GZIPInputStream(
new BufferedInputStream(new FileInputStream(gzipFilePath)));
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(destFilePath))) {
byte[] buffer = new byte[1024];
int len;
while ((len = gzis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
}
}
public static void main(String[] args) throws IOException {
ungzip("data.txt.gz", "data_ungzipped.txt");
System.out.println("GZIP 解压缩完成!");
}
}
ZIP 与 GZIP 的对比
| 特性 | ZIP | GZIP |
|---|---|---|
| 支持对象 | 多文件、文件夹(保留目录结构) | 仅单个文件 |
| 压缩效率 | 适中(支持存储未压缩文件) | 较高(通常比 ZIP 压缩率更高) |
| 用途 | 多文件归档(如软件安装包、资源包) | 单个大文件压缩(如日志、备份文件) |
| 实现复杂度 | 较高(需处理条目和目录结构) | 较低(直接读写文件内容) |
| 常见扩展名 | .zip |
.gz |
最佳实践
- 缓冲流提升性能:压缩 / 解压缩时,使用
BufferedInputStream和BufferedOutputStream减少 IO 次数,显著提升性能。 - 处理中文乱码:ZIP 操作需指定编码(如
UTF-8),避免条目名称或内容乱码。 - 递归压缩文件夹:ZIP 压缩文件夹时,需递归处理子文件和子目录,确保目录结构完整。
- 异常处理:使用 try-with-resources 自动关闭流,避免资源泄露;捕获 IO 异常并友好提示。
- 大文件处理:对于 GB 级大文件,可增大缓冲区大小(如 8KB 或 16KB),平衡内存占用和效率