You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 09:00:57