1. 概述
在开发中,我们经常需要压缩文件以节省空间或方便传输。Zip是压缩领域最常用的归档格式之一。
Java提供了ZipFile
和ZipInputStream
两个标准类来处理zip文件。本文将深入探讨两者的使用方法、功能差异和性能表现,帮你避免踩坑。
2. 创建Zip文件
在演示读取zip文件之前,先简单回顾下创建zip文件的过程:
String data = "..."; // 超长字符串
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
ZipOutputStream zos = new ZipOutputStream(bos)) {
ZipEntry zipEntry = new ZipEntry("zip-entry.txt");
zos.putNextEntry(zipEntry);
zos.write(data);
zos.closeEntry();
}
这段代码将数据压缩到名为zip-entry.txt
的zip条目中,并写入目标文件。基础操作,有经验的开发者应该很熟悉。
3. 使用ZipFile读取
先看如何用ZipFile
读取zip文件中的所有条目:
try (ZipFile zipFile = new ZipFile(compressedFile)) {
Enumeration<? extends ZipEntry> zipEntries = zipFile.entries();
while (zipEntries.hasMoreElements()) {
ZipEntry zipEntry = zipEntries.nextElement();
try (InputStream inputStream = new BufferedInputStream(zipFile.getInputStream(zipEntry))) {
// 从InputStream读取数据
}
}
}
核心步骤:
- 创建
ZipFile
实例 - 通过
entries()
获取所有条目 - 遍历条目并获取
InputStream
读取内容
除了entries()
,ZipFile
还提供getEntry(...)
方法实现随机访问:
ZipEntry zipEntry = zipFile.getEntry("str-data-10.txt");
try (InputStream inputStream = new BufferedInputStream(zipFile.getInputStream(zipEntry))) {
// 从InputStream读取数据
}
4. 使用ZipInputStream读取
再看ZipInputStream
的典型用法:
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(compressedFile));
ZipInputStream zipInputStream = new ZipInputStream(bis)) {
ZipEntry zipEntry;
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
// 从ZipInputStream读取数据
}
}
关键点:
- 用
ZipInputStream
包装输入流 - 通过
getNextEntry()
遍历条目 - 在循环中直接从流读取数据
⚠️ 注意:读取完一个条目后必须调用getNextEntry()
才能继续处理下一个条目。
5. 功能差异
虽然两者都能读取zip文件,但存在两个关键差异:
5.1. 访问方式
✅ ZipFile:支持随机访问
❌ ZipInputStream:仅支持顺序访问
ZipFile
可以直接通过getEntry()
定位特定条目,这在只需要处理部分文件时特别高效。而ZipInputStream
必须遍历整个流才能找到目标条目,简单粗暴但不够灵活。
5.2. 数据源要求
✅ ZipFile:必须基于物理文件
❌ ZipInputStream:只需InputStream
当数据来自网络流等非文件源时,ZipInputStream
是唯一选择。而ZipFile
需要先将流保存为临时文件才能处理,增加了额外开销。
6. 性能对比
使用JMH进行基准测试,先添加Maven依赖:
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.37</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.37</version>
</dependency>
6.1. 读取所有条目
测试场景:包含10个条目的zip文件,每个条目200KB数据
类 | 耗时(毫秒) |
---|---|
ZipFile |
11.072 |
ZipInputStream |
11.642 |
结论:两者性能差异不大(<10%),读取全部条目时效率相当。
6.2. 读取最后一个条目
类 | 耗时(毫秒) |
---|---|
ZipFile |
1.016 |
ZipInputStream |
12.830 |
结果惊人:
ZipFile
耗时仅为读取全部条目的1/10ZipInputStream
耗时与读取全部条目基本相同
这验证了ZipInputStream
的顺序访问特性:必须从头扫描到目标位置。而ZipFile
可以直接跳转到目标条目,在处理大文件中的少量条目时优势明显。
7. 总结
Java处理zip文件时,ZipFile
和ZipInputStream
的选择取决于具体场景:
✅ 优先选择ZipFile
当:
- 需要处理大文件中的少量条目
- 数据源是物理文件
- 对性能有较高要求
✅ 选择ZipInputStream
当:
- 数据源是网络流等非文件源
- 需要顺序处理所有条目
- 对内存敏感(无需加载整个文件)
实际开发中,根据需求灵活选择才能避免性能陷阱。完整示例代码可在GitHub获取。