Java案例如何实现压缩文件解压?

wen java案例 3

本文目录导读:

Java案例如何实现压缩文件解压?

  1. 解压 Zip 文件 (Java原生实现)
  2. 解压 RAR 文件 (使用 Apache Commons Compress)
  3. 解压常见问题的总结与建议

Java中实现压缩文件解压,最常用的就是 Zip 格式,Java标准库中的 java.util.zip 包提供了原生支持,对于 RAR 格式,Java标准库不支持,需要借助第三方库(如 Apache Commons Compress 或 junrar)。

下面我为你提供 Java 解压 ZipRAR 文件的核心案例代码。

解压 Zip 文件 (Java原生实现)

这是最常用且无需引入任何第三方依赖的方法。

import java.io.*;
import java.nio.charset.Charset;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class ZipDecompressor {
    /**
     * 解压ZIP文件
     * @param zipFilePath    要解压的zip文件路径
     * @param destDirPath    解压到目标目录的路径
     * @param charset        编码格式 (中文文件名建议使用 GBK 或 UTF-8)
     * @throws IOException   如果发生I/O错误
     */
    public static void unzip(String zipFilePath, String destDirPath, Charset charset) throws IOException {
        File destDir = new File(destDirPath);
        // 如果目标目录不存在,则创建
        if (!destDir.exists()) {
            destDir.mkdirs();
        }
        // 使用 ZipInputStream 读取文件 (推荐处理大文件)
        try (ZipInputStream zis = new ZipInputStream(
                new FileInputStream(zipFilePath), charset)) {
            ZipEntry entry;
            byte[] buffer = new byte[1024];
            // 遍历zip内的每一个条目
            while ((entry = zis.getNextEntry()) != null) {
                // 构建目标文件路径 (注意防止路径穿越漏洞)
                File newFile = new File(destDir, entry.getName());
                String destDirCanonical = destDir.getCanonicalPath();
                String newFileCanonical = newFile.getCanonicalPath();
                // 安全检查:防止压缩包内包含 ".." 导致的路径穿越
                if (!newFileCanonical.startsWith(destDirCanonical + File.separator)) {
                    throw new IOException("Entry is outside of the target dir: " + entry.getName());
                }
                if (entry.isDirectory()) {
                    // 如果是目录,创建目录
                    newFile.mkdirs();
                } else {
                    // 如果是文件,确保父目录存在
                    newFile.getParentFile().mkdirs();
                    // 写入文件内容
                    try (FileOutputStream fos = new FileOutputStream(newFile)) {
                        int len;
                        while ((len = zis.read(buffer)) > 0) {
                            fos.write(buffer, 0, len);
                        }
                    }
                }
                zis.closeEntry(); // 关闭当前条目
            }
        }
    }
    // 简便方法 (默认UTF-8编码)
    public static void unzip(String zipFilePath, String destDirPath) throws IOException {
        unzip(zipFilePath, destDirPath, Charset.forName("UTF-8"));
    }
    // 测试示例
    public static void main(String[] args) {
        try {
            String zipFile = "D:/test/example.zip";
            String destDir = "D:/test/output/";
            // 尝试UTF-8 (Linux/Mac) 或 GBK (Windows中文系统)
            try {
                unzip(zipFile, destDir, Charset.forName("UTF-8"));
            } catch (Exception e) {
                System.out.println("UTF-8解压失败,尝试GBK...");
                unzip(zipFile, destDir, Charset.forName("GBK"));
            }
            System.out.println("解压完成!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

代码要点说明:

  • 路径穿越防护ZipSlip漏洞是解压时常见的安全问题,恶意压缩包可能包含 文件名覆盖其他文件。
  • 编码问题:Windows上很多压缩软件使用GBK编码,Linux/Mac使用UTF-8,建议先尝试UTF-8,失败再尝试GBK。
  • ZipInputStream:适合大文件,流式读取,不会把所有文件加载到内存。

解压 RAR 文件 (使用 Apache Commons Compress)

RAR是专有格式,Java标准库不支持,推荐使用 org.apache.commons.compress

Maven依赖 (pom.xml):

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-compress</artifactId>
    <version>1.26.2</version> <!-- 请使用最新稳定版 -->
</dependency>

代码示例:

import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.ArchiveStreamFactory;
import org.apache.commons.compress.archivers.rar.RarArchiveInputStream;
import org.apache.commons.compress.utils.IOUtils;
import java.io.*;
public class RarDecompressor {
    public static void unrar(String rarFilePath, String destDirPath) throws Exception {
        File destDir = new File(destDirPath);
        if (!destDir.exists()) {
            destDir.mkdirs();
        }
        // 创建解压输入流
        try (ArchiveInputStream ais = new ArchiveStreamFactory()
                .createArchiveInputStream(ArchiveStreamFactory.RAR,
                        new BufferedInputStream(new FileInputStream(rarFilePath)))) {
            ArchiveEntry entry;
            while ((entry = ais.getNextEntry()) != null) {
                // 构建目标文件路径 (同样需要路径穿越防护)
                File newFile = new File(destDir, entry.getName());
                String destDirCanonical = destDir.getCanonicalPath();
                String newFileCanonical = newFile.getCanonicalPath();
                if (!newFileCanonical.startsWith(destDirCanonical + File.separator)) {
                    throw new IOException("Entry is outside of the target dir: " + entry.getName());
                }
                if (entry.isDirectory()) {
                    newFile.mkdirs();
                } else {
                    newFile.getParentFile().mkdirs();
                    // 使用Commons Compress提供的IOUtils简化写入
                    try (OutputStream os = new FileOutputStream(newFile)) {
                        IOUtils.copy(ais, os);
                    }
                }
            }
        }
    }
    // 测试示例
    public static void main(String[] args) {
        try {
            unrar("D:/test/example.rar", "D:/test/output_rar/");
            System.out.println("RAR解压完成!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

注意:

  • RarArchiveInputStream 目前只能解压 RAR v3 格式,对于 RAR v5 格式,commons-compress 的纯Java实现支持有限,可能需要使用 junrar 或调用系统 unrar 命令行。

解压常见问题的总结与建议

文件格式 推荐方案 注意事项
.zip 标准库 java.util.zip 注意编码(GBK/UTF-8);注意路径穿越漏洞
.tar.gz / .tar.bz2 Apache Commons Compress TarArchiveInputStream + GzipCompressorInputStream
.7z Apache Commons Compress (需额外依赖 xzlzma) 依赖于 org.tukaani:xz
.rar Apache Commons Compress / junrar RAR v4支持较好,RAR v5可能需用命令行

性能建议:

  • 对于超大文件 (如几百MB以上的ZIP),使用 ZipInputStream 流式处理,避免使用 ZipFile 一次性加载条目索引。
  • 对于多文件压缩包,可以考虑使用多线程解压不同文件(注意线程安全,每个文件单独输出流)。

如果你有具体的文件格式或特殊场景(如加密的ZIP、分卷压缩),欢迎进一步提问,我可以给出更针对性的方案。

抱歉,评论功能暂时关闭!