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

C# GZipStream 壓縮解壓縮陷阱:必須 Close 才能正確讀取

     編輯:關於C#

問題現象

使用 GZipStream 壓縮小資料 (< 4KB) 後,解壓縮時 Read 一直回傳 0,無法取得資料。大資料則正常。

錯誤程式碼


public static byte[] Compress(byte[] data)
{
    using var ms = new MemoryStream();
    using var gzip = new GZipStream(ms, CompressionMode.Compress);
    gzip.Write(data, 0, data.Length);
    // 錯誤:未 Close/Flush gzip 直接取 ms.ToArray()
    return ms.ToArray(); // 資料不完整!
}

public static byte[] Decompress(byte[] data)
{
    using var ms = new MemoryStream();
    using var gzip = new GZipStream(new MemoryStream(data), CompressionMode.Decompress);
    byte[] buffer = new byte[4096];
    int n;
    while ((n = gzip.Read(buffer, 0, buffer.Length)) > 0)
        ms.Write(buffer, 0, n);
    return ms.ToArray();
}

根本原因

GZipStream 採用**區塊式壓縮**,內部緩衝區預設 4KB。**資料未滿區塊時,必須呼叫 Close() 或 Dispose() 觸發最後區塊刷新並寫入 GZIP 尾部資訊 (CRC、原始大小)**。
using 區塊結束會自動呼叫 Dispose(),**但順序關鍵**:必須先 Dispose() GZipStream,**再**讀取 MemoryStream.ToArray()。

正確程式碼


public static byte[] Compress(byte[] data)
{
    using var ms = new MemoryStream();
    using (var gzip = new GZipStream(ms, CompressionMode.Compress))
    {
        gzip.Write(data, 0, data.Length);
    } // gzip.Dispose() 在此處執行,刷新最後區塊
    return ms.ToArray(); // 此時資料完整
}

public static byte[] Decompress(byte[] data)
{
    using var inputMs = new MemoryStream(data);
    using var gzip = new GZipStream(inputMs, CompressionMode.Decompress);
    using var outputMs = new MemoryStream();
    gzip.CopyTo(outputMs); // .NET 4+ 內建 CopyTo,簡潔高效
    return outputMs.ToArray();
}

關鍵點總結

錯誤做法正確做法
gzip.Write(...); return ms.ToArray();using (gzip) { gzip.Write(...); } return ms.ToArray();
手動 gzip.Flush()Flush() 僅刷緩衝區,**不寫入 GZIP 尾部**,仍不完整
gzip.Close() 後繼續用 gzipClose() = Dispose(),物件不可再用

現代替代方案

  • System.IO.Compression.ZLibStream (.NET 6+):ZLIB 封裝,無 GZIP 頭尾,適合串流傳輸。
  • BrotliStream (.NET Core 2.1+):壓縮率優於 GZIP,速度相當,CompressionMode.Compress/Decompress 相同 API。
  • 第三方庫:SharpZipLib, ZstdNet (Zstandard),功能更豐富。

單元測試驗證


[Test]
public void CompressDecompress_RoundTrip_SmallData()
{
    byte[] original = "Small data"u8.ToArray();
    byte[] compressed = GZipHelper.Compress(original);
    byte[] decompressed = GZipHelper.Decompress(compressed);
    Assert.That(decompressed, Is.EqualTo(original));
}

[Test]
public void CompressDecompress_RoundTrip_LargeData()
{
    byte[] original = new byte[10000];
    new Random(42).NextBytes(original);
    byte[] compressed = GZipHelper.Compress(original);
    byte[] decompressed = GZipHelper.Decompress(compressed);
    Assert.That(decompressed, Is.EqualTo(original));
}

記住:**GZipStream 必須 Dispose 後才能取得完整壓縮資料**,這是許多開發者踩過的坑。

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