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

Java GZIP 壓縮/解壓縮工具類完整實現:流式處理、檔案/位元組陣列雙模式

     編輯:Java 相關知識

前言:為什麼需要 GZIP 工具類?

GZIP 是 HTTP、日誌、訊息隊列、物件序列化最常用的無損壓縮格式。JDK java.util.zip.GZIPInputStream/OutputStream 提供流式 API,但直接使用時易漏關閉資源、緩衝區大小固定、檔案與位元組陣列需重寫邏輯。本文封裝一個執行緒安全、資源自動管理、支援檔案/位元組雙模式、可選擇刪除源檔的生產級工具類 GZipUtils。

完整工具類程式碼


package com.cjonline.foundation.cpe.action;

import java.io.*;
import java.util.zip.*;

public abstract class GZipUtils {
    public static final int BUFFER = 8192; // 8KB,平衡記憶體與系統呼叫
    public static final String EXT = ".gz";

    /* ========== 位元組陣列模式 ========== */
    public static byte[] compress(byte[] data) throws IOException {
        try (ByteArrayInputStream bis = new ByteArrayInputStream(data);
             ByteArrayOutputStream bos = new ByteArrayOutputStream();
             GZIPOutputStream gos = new GZIPOutputStream(bos)) {
            copy(bis, gos);
            gos.finish(); // 關鍵:寫入 GZIP footer
            return bos.toByteArray();
        }
    }

    public static byte[] decompress(byte[] data) throws IOException {
        try (ByteArrayInputStream bis = new ByteArrayInputStream(data);
             ByteArrayOutputStream bos = new ByteArrayOutputStream();
             GZIPInputStream gis = new GZIPInputStream(bis)) {
            copy(gis, bos);
            return bos.toByteArray();
        }
    }

    /* ========== 檔案模式 ========== */
    public static void compress(File file) throws IOException { compress(file, true); }
    public static void compress(File file, boolean deleteSource) throws IOException {
        File out = new File(file.getAbsolutePath() + EXT);
        try (FileInputStream fis = new FileInputStream(file);
             FileOutputStream fos = new FileOutputStream(out);
             GZIPOutputStream gos = new GZIPOutputStream(fos)) {
            copy(fis, gos);
            gos.finish();
        }
        if (deleteSource && !file.delete()) throw new IOException("無法刪除源檔: " + file);
    }

    public static void decompress(File file) throws IOException { decompress(file, true); }
    public static void decompress(File file, boolean deleteSource) throws IOException {
        if (!file.getName().endsWith(EXT)) throw new IllegalArgumentException("非 .gz 檔案");
        File out = new File(file.getAbsolutePath().substring(0, file.getAbsolutePath().length() - EXT.length()));
        try (FileInputStream fis = new FileInputStream(file);
             FileOutputStream fos = new FileOutputStream(out);
             GZIPInputStream gis = new GZIPInputStream(fis)) {
            copy(gis, fos);
        }
        if (deleteSource && !file.delete()) throw new IOException("無法刪除源檔: " + file);
    }

    // 路徑字串重載
    public static void compress(String path) throws IOException { compress(new File(path)); }
    public static void compress(String path, boolean delete) throws IOException { compress(new File(path), delete); }
    public static void decompress(String path) throws IOException { decompress(new File(path)); }
    public static void decompress(String path, boolean delete) throws IOException { decompress(new File(path), delete); }

    /* ========== 核心流拷貝 ========== */
    private static void copy(InputStream in, OutputStream out) throws IOException {
        byte[] buf = new byte[BUFFER];
        int len;
        while ((len = in.read(buf)) != -1) out.write(buf, 0, len);
    }
}

設計亮點解析

特性實現細節收益
try-with-resources所有 Stream 宣告在 try() 區塊異常/正常返回均自動關閉,零洩漏
緩衝區 8KBBUFFER = 8192減少系統呼叫,適配磁碟頁面/網路 MTU
gos.finish()壓縮結束前呼叫確保寫入 CRC-32、ISIZE 等 GZIP footer,否則解壓報 "not in GZIP format"
刪除源檔可選deleteSource 參數支援「壓縮備份」與「壓縮替換」兩種業務場景
輸入校驗解壓檢查 .gz 副檔名快速失敗,避免誤操作非 GZIP 檔案
執行緒安全無共享可變狀態,所有區域變數可直接在並發環境共用

使用範例


// 1. 位元組陣列壓縮/解壓 (如 Redis 快取、訊息體)
byte[] compressed = GZipUtils.compress(json.getBytes(StandardCharsets.UTF_8));
byte[] original = GZipUtils.decompress(compressed);

// 2. 檔案壓縮備份 (保留源檔)
GZipUtils.compress(new File("/var/log/app.log"), false);

// 3. 檔案解壓並刪除 .gz
GZipUtils.decompress("/tmp/data.gz", true);

// 4. 大檔案流式處理 (不載入記憶體)
GZipUtils.compress(new File("huge.csv"), new File("huge.csv.gz")); // 需自行擴充多參數版本

效能與替代方案對比

方案壓縮率速度CPU適用場景
JDK GZIP (DEFLATE)中等中等中通用、相容性最好、HTTP 標準
Snappy較低極快低即時壓縮、Kafka/RocksDB 內建
LZ4較低極快極低高吞吐日誌、資料庫頁面壓縮
Zstandard (zstd)高快中現代替代者,支援字典、多執行緒
若專案對壓縮率/速度敏感,建議引入 lz4-java 或 zstd-jni,API 與 GZIP 相似,只需替換 Stream 實現類。

常見錯誤與排查

  1. 解壓報 "Not in GZIP format":輸入資料非 GZIP、或壓縮時漏呼叫 finish()、或雙重壓縮。
  2. 大檔案 OOM:位元組陣列模式會全載入記憶體,大檔案必須用檔案流模式。
  3. 壓縮後檔案更大:已壓縮/加密/高熵資料(如 JPG、MP4、JAR)再壓縮會因 Header 開銷變大,建議先檢測熵值決定是否壓縮。
  4. 中文檔名亂碼:GZIP 單檔不儲存檔名,跨平台解壓檔名由外層(如 tar、zip、HTTP Header)決定。

結語

一個健壯的 GZipUtils 能讓團隊在日誌輪轉、快取壓縮、API 傳輸、檔案備份等場景「拿來即用、零 Bug」。核心原則:**流式處理、try-with-resources、finish() 收尾、緩衝區適中、輸入校驗、執行緒安全**理解這些細節,也能輕鬆遷移到 LZ4、Zstd 等現代壓縮算法。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved