1. 概述

在开发中,我们经常需要压缩文件以节省空间或方便传输。Zip是压缩领域最常用的归档格式之一

Java提供了ZipFileZipInputStream两个标准类来处理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读取数据
        }
    }
}

核心步骤:

  1. 创建ZipFile实例
  2. 通过entries()获取所有条目
  3. 遍历条目并获取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读取数据
    }
}

关键点:

  1. ZipInputStream包装输入流
  2. 通过getNextEntry()遍历条目
  3. 在循环中直接从流读取数据

⚠️ 注意:读取完一个条目后必须调用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/10
  • ZipInputStream耗时与读取全部条目基本相同

这验证了ZipInputStream的顺序访问特性:必须从头扫描到目标位置。而ZipFile可以直接跳转到目标条目,在处理大文件中的少量条目时优势明显

7. 总结

Java处理zip文件时,ZipFileZipInputStream的选择取决于具体场景:

优先选择ZipFile

  • 需要处理大文件中的少量条目
  • 数据源是物理文件
  • 对性能有较高要求

选择ZipInputStream

  • 数据源是网络流等非文件源
  • 需要顺序处理所有条目
  • 对内存敏感(无需加载整个文件)

实际开发中,根据需求灵活选择才能避免性能陷阱。完整示例代码可在GitHub获取。


原始标题:Difference Between ZipFile and ZipInputStream in Java

» 下一篇: Java Weekly, Issue 514