首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 解壓 ZIP 文件完整指南:核心代碼、編碼處理與最佳實踐

     編輯:Java 相關知識

核心依賴選擇

推薦方案:Ant Zip 處理中文路徑

<dependency>
    <groupId>org.apache.ant</groupId>
    <artifactId>ant</artifactId>
    <version>1.10.14</version>
</dependency>

完整解壓工具類

import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Enumeration;

public class ZipUtils {
    
    /**
     * 解壓 ZIP 文件到指定目錄
     * @param zipPath ZIP 文件路徑
     * @param destDir 目標目錄
     * @param charset 編碼 (建議 GBK/UTF-8)
     * @param deleteAfter 是否解壓後刪除原文件
     * @throws IOException
     */
    public static void unzip(String zipPath, String destDir, String charset, boolean deleteAfter) 
            throws IOException {
        
        File zipFile = new File(zipPath);
        if (!zipFile.exists()) {
            throw new FileNotFoundException("ZIP 文件不存在: " + zipPath);
        }
        
        File destDirectory = new File(destDir);
        if (!destDirectory.exists()) {
            destDirectory.mkdirs();
        }
        
        try (ZipFile zip = new ZipFile(zipFile, charset)) {
            Enumeration entries = zip.getEntries();
            
            while (entries.hasMoreElements()) {
                ZipEntry entry = entries.nextElement();
                File targetFile = new File(destDirectory, entry.getName());
                
                // 安全檢查:防止 Zip Slip 攻擊 (目錄遍歷)
                if (!targetFile.getCanonicalPath().startsWith(destDirectory.getCanonicalPath())) {
                    throw new SecurityException("檢測到惡意路徑遍歷: " + entry.getName());
                }
                
                if (entry.isDirectory()) {
                    targetFile.mkdirs();
                    continue;
                }
                
                // 確保父目錄存在
                File parent = targetFile.getParentFile();
                if (parent != null && !parent.exists()) {
                    parent.mkdirs();
                }
                
                // 流式複製,避免大文件內存溢出
                try (InputStream is = zip.getInputStream(entry);
                     FileOutputStream fos = new FileOutputStream(targetFile);
                     BufferedOutputStream bos = new BufferedOutputStream(fos)) {
                    
                    byte[] buffer = new byte[8192];
                    int len;
                    while ((len = is.read(buffer)) != -1) {
                        bos.write(buffer, 0, len);
                    }
                    bos.flush();
                }
            }
        }
        
        if (deleteAfter) {
            Files.deleteIfExists(zipFile.toPath());
        }
    }
    
    /**
     * 解壓單個文件到輸出流 (適合大文件、無需落地)
     */
    public static void unzipToStream(String zipPath, String entryName, OutputStream out, String charset) 
            throws IOException {
        try (ZipFile zip = new ZipFile(new File(zipPath), charset)) {
            ZipEntry entry = zip.getEntry(entryName);
            if (entry == null) throw new FileNotFoundException("條目不存在: " + entryName);
            
            try (InputStream is = zip.getInputStream(entry);
                 BufferedInputStream bis = new BufferedInputStream(is)) {
                byte[] buffer = new byte[8192];
                int len;
                while ((len = bis.read(buffer)) != -1) {
                    out.write(buffer, 0, len);
                }
            }
        }
    }
}

關鍵問題與解決方案

1. 中文文件名亂碼

原因:JDK 原生 ZipFile 僅支援 UTF-8,Windows 建立的 ZIP 通常用 GBK。
解決:使用 org.apache.tools.zip.ZipFile 並指定 charset="GBK"。

2. Zip Slip 安全漏洞

原理:惡意 ZIP 包含 `../../etc/passwd` 等路徑,解壓時跳出目標目錄。
防禦:必須驗證解壓後路徑在目標目錄內(見代碼 canonicalPath 檢查)。

3. 大文件內存溢出 (OOM)

錯誤做法:`byte[] data = IOUtils.toByteArray(is)` 一次性讀入內存。
正確做法:分塊流式讀寫(見代碼 8KB buffer)。

4. 空目錄處理

ZIP 條目以 `/` 結尾表示目錄,需顯式 `mkdirs()`,否則空目錄會丟失。

Spring Boot 整合示例

@Service
public class FileStorageService {
    
    @Value("${storage.temp-dir}")
    private String tempDir;
    
    public void extractUploadedZip(MultipartFile zipFile, String targetDir) throws IOException {
        File tempZip = File.createTempFile("upload-", ".zip");
        try {
            zipFile.transferTo(tempZip);
            ZipUtils.unzip(tempZip.getAbsolutePath(), targetDir, "GBK", false);
        } finally {
            tempZip.delete(); // 確保臨時文件清理
        }
    }
}

總結

Java 解壓 ZIP 看似簡單,實則暗藏編碼、安全、內存、空目錄等坑點。掌握 Ant Zip + 流式處理 + 路徑校驗 + 異常安全關閉,才是生產級的正確姿勢。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
庫特點推薦場景
java.util.zipJDK 原生、無依賴簡單場景、無中文路徑
Apache Commons Compress功能豐富、支援多格式企業級應用、多格式需求
Apache Ant Zip (org.apache.tools.zip)支援 GBK 編碼、解決中文亂碼**中文文件名場景首選**
Zip4j支援加密、分卷、流式處理加密 ZIP、大文件處理