Java 压缩与解压缩操作详解:ZIP 与 GZIP 实践
在 Java 中,处理压缩文件是常见需求,尤其是在文件传输、存储优化等场景。JDK 内置了对 ZIP 和 GZIP 格式的支持,通过 java.util.zip 包中的类可实现压缩和解压缩操作。本文将详细介绍 ZIP 和 GZIP 的使用方法,包括单文件 / 多文件压缩、文件夹递归压缩及解压缩的具体实现。
ZIP 压缩与解压缩
ZIP 是一种广泛使用的归档格式,支持将多个文件或文件夹压缩到一个 .zip 文件中,且可保留目录结构。Java 中通过 ZipInputStream(解压缩)和 ZipOutputStream(压缩)实现 ZIP 操作。
ZipInputStream 用于读取 ZIP 文件中的条目(文件或文件夹),通过 getNextEntry() 遍历所有条目,再通过输入流读取具体内容。
核心步骤:
- 创建
ZipInputStream 关联 ZIP 文件输入流。
- 循环调用
getNextEntry() 获取每个 ZipEntry(条目)。
- 对每个条目,通过
ZipInputStream 读取数据(若为文件夹则跳过内容读取)。
- 处理完成后调用
closeEntry() 关闭当前条目,最终关闭流。
示例:解压缩 ZIP 文件到指定目录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| import java.io.*; import java.nio.charset.StandardCharsets; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream;
public class ZipUncompressor {
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
| import java.io.*; import java.nio.charset.StandardCharsets; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream;
public class ZipCompressor {
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, ""); } } }
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); } } }
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import java.io.*; import java.util.zip.GZIPOutputStream;
public class GzipCompressor {
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 文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import java.io.*; import java.util.zip.GZIPInputStream;
public class GzipUncompressor {
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),平衡内存占用和效率
v1.3.10