在網路傳輸、檔案儲存、快取系統中,資料壓縮能大幅降低頻寬與空間成本。Java 核心套件 java.util.zip 提供基於 DEFLATE 演算法(LZ77 + Huffman 編碼)的 Deflater(壓縮)與 Inflater(解壓縮),無需第三方庫即可使用。本文提供生產級工具類實作與關鍵細節解析。
| 類別 | 關鍵方法 | 說明 |
|---|---|---|
| Deflater | setInput(byte[]) | 設定待壓縮資料 |
| finish() | 標記輸入結束,準備輸出剩餘壓縮資料 | |
| deflate(byte[]) | 執行壓縮,回傳寫入位元組數 | |
| end() | 釋放原生資源 | |
| Inflater | setInput(byte[]) | 設定待解壓資料 |
| inflate(byte[]) | 執行解壓,回傳寫入位元組數 | |
| finished() | 判斷解壓是否完成 | |
| end() | 釋放原生資源 |
import java.io.*;
import java.util.zip.*;
import org.apache.commons.codec.binary.Base64; // 需 commons-codec 依賴
public class DeflateUtils {
/** 壓縮位元組陣列 */
public static byte[] compress(byte[] input) throws IOException {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
Deflater deflater = new Deflater();
deflater.setInput(input);
deflater.finish(); // 關鍵:告知無後續輸入
byte[] buffer = new byte[1024];
while (!deflater.finished()) {
int len = deflater.deflate(buffer);
if (len > 0) bos.write(buffer, 0, len);
}
deflater.end();
return bos.toByteArray();
}
}
/** 解壓縮位元組陣列 */
public static byte[] decompress(byte[] input) throws IOException {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
Inflater inflater = new Inflater();
inflater.setInput(input);
byte[] buffer = new byte[1024];
while (!inflater.finished()) {
int len = inflater.inflate(buffer);
if (len <= 0) break; // 防禦性檢查
bos.write(buffer, 0, len);
}
inflater.end();
return bos.toByteArray();
}
}
/** 壓縮 + Base64 編碼(適合網路傳輸/JSON 嵌入) */
public static String compressToBase64(byte[] input) throws IOException {
return Base64.encodeBase64String(compress(input));
}
/** Base64 解碼 + 解壓縮 */
public static byte[] decompressFromBase64(String base64) throws IOException {
return decompress(Base64.decodeBase64(base64));
}
// --- 檔案層級便利方法 ---
public static void compressFile(File src, File dest) throws IOException {
try (FileInputStream fis = new FileInputStream(src);
FileOutputStream fos = new FileOutputStream(dest)) {
byte[] data = fis.readAllBytes(); // Java 9+
fos.write(compress(data));
}
}
public static void decompressFile(File src, File dest) throws IOException {
try (FileInputStream fis = new FileInputStream(src);
FileOutputStream fos = new FileOutputStream(dest)) {
byte[] data = fis.readAllBytes();
fos.write(decompress(data));
}
}
}
Deflater 採串流模式,finish() 告知「輸入結束」,才會輸出最後壓縮區塊與校驗碼。漏呼叫會導致輸出不完整、解壓失敗。
deflate() 回傳 0 不代表結束,可能只是緩衝區未滿。必須配合 finished() 判斷。
Deflater/Inflater 底層使用 JNI 持有原生記憶體,必須在 finally 區塊呼叫 end(),否則洩漏非堆記憶體。
1024 位元組為保守值。大檔案建議 8KB~64KB 減少 JNI 呼叫開銷:
byte[] buffer = new byte[32 * 1024]; // 32KB
Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION); // 9: 最高壓縮率,最慢
// Deflater.BEST_SPEED (1), Deflater.DEFAULT_COMPRESSION (-1), Deflater.NO_COMPRESSION (0)
壓縮後再 Base64 編碼體積會增大 1/3,若傳輸協定支援二進位(如 HTTP Body、gRPC),直接傳輸位元組陣列 更高效。
public class DeflateDemo {
public static void main(String[] args) throws IOException {
String original = "Java Deflate 壓縮測試 ".repeat(1000); // ~20KB 文字
byte[] input = original.getBytes(StandardCharsets.UTF_8);
// 壓縮
byte[] compressed = DeflateUtils.compress(input);
System.out.printf("原始: %,d bytes -> 壓縮: %,d bytes (%.1f%%)%n",
input.length, compressed.length, 100.0 * compressed.length / input.length);
// Base64 編碼模擬網路傳輸
String b64 = DeflateUtils.compressToBase64(input);
System.out.println("Base64 長度: " + b64.length());
// 解壓驗證
byte[] decompressed = DeflateUtils.decompressFromBase64(b64);
String restored = new String(decompressed, StandardCharsets.UTF_8);
System.out.println("內容一致: " + original.equals(restored));
}
}
典型輸出:
原始: 20,000 bytes -> 壓縮: 1,245 bytes (6.2%)
Base64 長度: 1,660
內容一致: true
避免一次性載入巨大檔案至記憶體,使用 DeflaterOutputStream/InflaterInputStream:
public static void compressStream(InputStream in, OutputStream out) throws IOException {
try (DeflaterOutputStream dos = new DeflaterOutputStream(out, new Deflater(Deflater.BEST_COMPRESSION))) {
in.transferTo(dos); // Java 9+
}
}
public static void decompressStream(InputStream in, OutputStream out) throws IOException {
try (InflaterInputStream iis = new InflaterInputStream(in)) {
iis.transferTo(out);
}
}
Deflater/Inflater 是 Java 內建、零依賴的 DEFLATE 實作。setInput → finish → 迴圈 deflate/inflate → end。finally 釋放 end(),防止原生記憶體洩漏。DeflaterOutputStream/InflaterInputStream。掌握此工具類,即可在微服務通訊、日誌壓縮、快取序列化等場景輕鬆套用高效能壓縮。