專案常需將資料夾打包成 ZIP 供下載或備份。Java java.util.zip 套件提供零依賴的壓縮能力,核心類別為 ZipOutputStream。本文提供一個可直接用於生產環境的遞迴壓縮工具類,支援多層目錄、空資料夾保留、大檔案分塊寫入,並妥善處理資源關閉與異常。
ZipOutputStream:輸出 ZIP 資料流,putNextEntry(ZipEntry) 開始新條目。ZipEntry:代表 ZIP 內單一檔案/目錄條目,名稱含相對路徑。FileInputStream:讀取來源檔案位元組。8192 bytes(8 KB),平衡記憶體與 I/O 效能。package com.example.util;
import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public final class ZipUtil {
private static final int BUFFER_SIZE = 8192;
private ZipUtil() {} // 工具類禁止實例化
/**
* 將來源檔案或目錄壓縮為 ZIP
* @param srcPath 來源路徑(檔案或資料夾)
* @param zipPath 目標 ZIP 檔案路徑
* @throws IOException 壓縮過程發生 I/O 錯誤
*/
public static void compress(String srcPath, String zipPath) throws IOException {
File srcFile = new File(srcPath);
if (!srcFile.exists()) {
throw new FileNotFoundException("來源不存在:" + srcPath);
}
try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipPath))) {
// 以來源檔名/目錄名作為 ZIP 內部根條目前綴
String rootName = srcFile.getName();
compressRecursive(srcFile, zos, rootName);
}
System.out.println("壓縮完成:" + zipPath);
}
private static void compressRecursive(File srcFile, ZipOutputStream zos, String entryName) throws IOException {
if (srcFile.isFile()) {
// 檔案:建立條目、分塊寫入
zos.putNextEntry(new ZipEntry(entryName));
try (FileInputStream fis = new FileInputStream(srcFile)) {
byte[] buffer = new byte[BUFFER_SIZE];
int len;
while ((len = fis.read(buffer)) > 0) {
zos.write(buffer, 0, len);
}
}
zos.closeEntry();
} else if (srcFile.isDirectory()) {
// 目錄:建立目錄條目(以 / 結尾),遞迴處理子項
if (!entryName.endsWith("/")) entryName += "/";
zos.putNextEntry(new ZipEntry(entryName));
zos.closeEntry();
File[] children = srcFile.listFiles();
if (children != null) {
for (File child : children) {
compressRecursive(child, zos, entryName + child.getName());
}
}
}
}
// 測試主程式
public static void main(String[] args) {
try {
compress("d:/chat", "d:/chat01.zip");
} catch (IOException e) {
e.printStackTrace();
}
}
}
ZipOutputStream 與 FileInputStream,杜絕資源洩漏。listFiles() 回傳空陣列時,上層已建立目錄條目,空資料夾自然保留。OutOfMemoryError,且不頻繁觸發 GC。IOException 讓上層決定重試或記錄,不吞噬錯誤。if (child.getName().matches(excludeRegex)) continue;。zos.setLevel(Deflater.BEST_COMPRESSION) 或 BEST_SPEED。ProgressListener 介面,寫入位元組時回報進度。ForkJoinPool 平行壓縮再合併(需臨時檔)。純 JDK 實現 ZIP 壓縮無需第三方庫,掌握 ZipOutputStream 遞迴寫入模式,即可輕鬆應對目錄打包、日誌歸檔、檔案下載等場景。記住「資源自動關閉、目錄條目加斜線、緩衝區適中」三要訣,程式碼既健壯又高效。