使用 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() 後繼續用 gzip | Close() = Dispose(),物件不可再用 |
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 後才能取得完整壓縮資料**,這是許多開發者踩過的坑。