如何通过commons-compress获取Zip文件解压进度信息?
追踪Commons-Compress解压Zip的进度方案
嘿,我完全懂你的困惑!commons-compress确实没有内置的进度追踪API,但咱们可以自己动手实现一个简单又好用的方案,不用纠结是不是漏看了文档~下面就一步步来解决这个问题:
核心思路
要展示解压进度,关键是要掌握两个数值:压缩包中所有文件的总字节数,以及当前已经解压的字节数。我们可以通过自定义一个包装输入流来实时统计已读取的字节,再结合总字节数计算进度百分比。
1. 计算压缩包总字节数
首先遍历Zip包中的所有条目,累加所有文件的大小(跳过目录,因为目录本身不占实际字节):
long totalBytes = 0; try (ZipArchiveInputStream zais = new ZipArchiveInputStream(new FileInputStream(zipFilePath))) { ZipArchiveEntry entry; while ((entry = zais.getNextZipEntry()) != null) { if (!entry.isDirectory()) { totalBytes += entry.getSize(); } } }
注意:如果Zip包中有加密条目或者特殊格式的文件,
entry.getSize()可能返回-1,这种情况下可以退而求其次用压缩包的总文件大小作为参考,虽然进度会有偏差,但大部分场景下足够用。
2. 实现进度追踪的输入流
我们自定义一个ProgressTrackingInputStream,它继承自FilterInputStream,每次读取字节时累加计数,并通过回调函数实时输出进度:
import org.apache.commons.compress.utils.IOUtils; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; public class ProgressTrackingInputStream extends FilterInputStream { private long bytesRead; private final long totalBytes; private final ProgressCallback callback; // 定义进度回调接口 public interface ProgressCallback { void onProgress(long bytesRead, long totalBytes); } public ProgressTrackingInputStream(InputStream in, long totalBytes, ProgressCallback callback) { super(in); this.totalBytes = totalBytes; this.callback = callback; this.bytesRead = 0; } @Override public int read() throws IOException { int result = super.read(); if (result != -1) { bytesRead++; callback.onProgress(bytesRead, totalBytes); } return result; } @Override public int read(byte[] b) throws IOException { int bytesReadThisTime = super.read(b); if (bytesReadThisTime != -1) { bytesRead += bytesReadThisTime; callback.onProgress(bytesRead, totalBytes); } return bytesReadThisTime; } @Override public int read(byte[] b, int off, int len) throws IOException { int bytesReadThisTime = super.read(b, off, len); if (bytesReadThisTime != -1) { bytesRead += bytesReadThisTime; callback.onProgress(bytesRead, totalBytes); } return bytesReadThisTime; } }
3. 结合进度流完成解压
最后在解压时,用这个自定义流包装ZipArchiveInputStream,并在回调中输出进度到控制台:
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class ZipExtractorWithProgress { public static void main(String[] args) throws IOException { String zipFilePath = "your-target.zip"; String outputDir = "./extracted-files"; // 先计算总字节数 long totalBytes = 0; try (ZipArchiveInputStream zais = new ZipArchiveInputStream(new FileInputStream(zipFilePath))) { ZipArchiveEntry entry; while ((entry = zais.getNextZipEntry()) != null) { if (!entry.isDirectory()) { totalBytes += entry.getSize(); } } } // 开始解压并追踪进度 try (ZipArchiveInputStream zais = new ZipArchiveInputStream(new FileInputStream(zipFilePath))) { ZipArchiveEntry entry; while ((entry = zais.getNextZipEntry()) != null) { if (entry.isDirectory()) { continue; } String outputPath = outputDir + File.separator + entry.getName(); // 创建父目录 new File(outputPath).getParentFile().mkdirs(); // 进度回调:控制台实时输出百分比 ProgressTrackingInputStream.ProgressCallback callback = (read, total) -> { if (total > 0) { double progress = (double) read / total * 100; // 用\r覆盖同一行,避免刷屏 System.out.printf("\r当前解压进度: %.2f%%", progress); } }; try (ProgressTrackingInputStream ptis = new ProgressTrackingInputStream(zais, totalBytes, callback); FileOutputStream fos = new FileOutputStream(outputPath)) { IOUtils.copy(ptis, fos); } } System.out.println("\n解压完成!"); } } }
额外说明
- 这个思路不仅适用于Zip,对于commons-compress支持的其他格式(如Tar、7z)也完全通用,只需要替换对应的ArchiveInputStream即可。
- 如果需要更优雅的进度展示(比如进度条),可以在回调函数中实现更复杂的控制台输出逻辑,或者集成到GUI界面中。
内容的提问来源于stack exchange,提问作者khmarbaise
相关产品推荐
相关产品推荐

