首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Java ZIP 壓縮程式碼深度解析:遞迴遍歷目錄、ZipEntry 與 base 參數詳解

     編輯:程式設計綜合問答

原始程式碼功能

遞迴將檔案/目錄壓縮為 ZIP 檔案,支援巢狀目錄結構。

核心參數 `base` 完整含義

`base` 代表「在 ZIP 檔案內部的相對路徑前綴」,用於構建每個條目 (ZipEntry) 的完整內部路徑。

  • 初始呼叫:`base = ""` (空字串,代表 ZIP 根目錄)
  • 進入子目錄:`base = "parentDir/"` → 子檔案路徑變為 `"parentDir/childFile.txt"`
  • 確保 ZIP 內部目錄結構與原始檔案系統一致

關鍵程式碼逐行解析

private void zip(ZipOutputStream out, File f, String base) throws Exception {
    if (f.isDirectory()) {                    // 1. 如果是目錄
        File[] fl = f.listFiles();            //    取得子檔案/目錄陣列
        out.putNextEntry(new ZipEntry(base + "/")); // 2. 建立目錄條目 (以 / 結尾)
        base = base.length() == 0 ? "" : base + "/"; // 3. 更新 base:加上目錄名 + /
        for (int i = 0; i < fl.length; i++) { // 4. 遞迴處理每個子項
            zip(out, fl[i], base + fl[i].getName());
        }
    } else {                                   // 5. 如果是檔案
        out.putNextEntry(new ZipEntry(base));  //    建立檔案條目 (base 已含完整相對路徑)
        FileInputStream in = new FileInputStream(f); //    開啟檔案輸入流
        int b;
        while ((b = in.read()) != -1) {        //    逐位元組讀取
            out.write(b);                      //    寫入 ZIP 輸出流
        }
        in.close();                            //    關閉輸入流
    }
}

三個關鍵問題詳細回答

1. `base = base.length() == 0 ? "" : base + "/";` 做什麼?

邏輯:

  • 若 `base` 為空字串 (根目錄) → 保持空字串
  • 若 `base` 非空 (如 `"parentDir"`) → 變為 `"parentDir/"`

目的:確保後續子項路徑正確拼接。
例子:目錄結構 `root/sub/file.txt`

遞迴層級fbase (呼叫前)base (更新後)子項傳入 base
1root/"""""" + "sub" = "sub"
2sub/"sub""sub/""sub/" + "file.txt" = "sub/file.txt"
3file.txt"sub/file.txt"--

2. `ZipEntry` 類別是什麼?

`ZipEntry` 代表 **ZIP 檔案內的一個條目 (檔案或目錄)**。它封裝了:

  • 條目名稱 (內部路徑,如 `"sub/file.txt"`) — 必填
  • 壓縮方法 (STORED/DEFLATED)、大小、CRC32、時間戳、註解等屬性

並非「建立檔案進入點」**,而是「描述一個將要寫入 ZIP 的條目元資料」。

3. `out.putNextEntry(new ZipEntry(base + "/"));` 執行了什麼操作?

  1. 建立一個名為 `base + "/"` (如 `"sub/"`) 的 **目錄條目** (以 `/` 結尾標識目錄)
  2. 通知 `ZipOutputStream` **開始寫入新條目** 的標頭資訊
  3. 後續 `out.write()` 的資料將屬於此條目,直到下一次 `putNextEntry()` 或 `closeEntry()`

**注意**:目錄條目通常**不寫入資料** (body 為空),僅建立目錄結構供解壓工具還原資料夾。

程式碼改進建議 (生產級)

// 使用 try-with-resources 確保資源關閉
// 使用緩衝區提升 I/O 效能 (避免逐位元組 read/write)
public void zip(File src, File zipFile) throws IOException {
    try (ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFile))) {
        zipRecursive(out, src, "", src.getName()); // 修正:根目錄名稱納入 base
    }
}

private void zipRecursive(ZipOutputStream out, File f, String base, String rootName) throws IOException {
    String entryName = base.isEmpty() ? rootName : base + "/" + rootName;
    
    if (f.isDirectory()) {
        // 目錄條目 (不寫入資料)
        out.putNextEntry(new ZipEntry(entryName + "/"));
        out.closeEntry();
        File[] children = f.listFiles();
        if (children != null) {
            for (File child : children) {
                zipRecursive(out, child, entryName, child.getName());
            }
        }
    } else {
        // 檔案條目
        out.putNextEntry(new ZipEntry(entryName));
        try (FileInputStream in = new FileInputStream(f)) {
            byte[] buffer = new byte[8192];
            int len;
            while ((len = in.read(buffer)) > 0) {
                out.write(buffer, 0, len);
            }
        }
        out.closeEntry();
    }
}

總結

  • `base` = **ZIP 內部相對路徑前綴**,遞迴構建完整條目路徑
  • `ZipEntry` = **條目元資料描述** (名稱、屬性),非實體檔案
  • `putNextEntry()` = **開啟新條目寫入模式**,目錄條目以 `/` 結尾且無資料體
  • 生產碼請使用**緩衝區**、**try-with-resources**、**正確根目錄處理**
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved