Web API、雲端函式、容器化服務常面臨「不寫入暫存檔、直接在記憶體/網路串流間壓縮解壓」的需求。SharpZipLib 完整支援 Stream 操作,配合 HttpClient、MinIO、Azure Blob 等物件儲存 SDK,即可實現零落地、低記憶體、高併發的線上壓縮解壓縮管線。本文示範三大核心場景:上傳即壓縮、下載即解壓、多檔案打包下載。
ZipOutputStream、GZipOutputStream、ZipInputStream...)皆繼承 Stream。await source.CopyToAsync(dest, bufferSize, cancellationToken),不載入完整檔案到記憶體,背壓自動調節。81920 (80KB),平衡系統呼叫與記憶體佔用。[HttpPost("upload-compress")]
[RequestSizeLimit(long.MaxValue)] // 關閉 Kestrel 預設 30MB 限制
public async Task UploadAndCompress(
IFormFile file,
[FromServices] IObjectStorage storage,
CancellationToken ct)
{
// 1. 開啟上游輸出串流(MinIO / S3 / Azure Blob / 本地檔案)
await using Stream destStream = await storage.OpenWriteAsync($"archive/{file.FileName}.gz", ct);
// 2. 包裝 GZip 壓縮串流
await using var gzStream = new GZipOutputStream(destStream) { IsStreamOwner = false };
gzStream.SetLevel(6); // 可選
// 3. 直接從請求串流複製到壓縮串流
await using Stream srcStream = file.OpenReadStream();
await srcStream.CopyToAsync(gzStream, 81920, ct);
// 4. 確保壓縮資料刷新到底層
await gzStream.FlushAsync(ct);
return Ok(new { message = "Compressed & stored", originalSize = file.Length });
}
關鍵:IsStreamOwner = false 讓 GZipOutputStream.Dispose() 不關閉> 物件儲存串流,由外層 using 統一管理。
[HttpGet("download-decompress/{objectKey}")]
public async Task DecompressAndDownload(string objectKey, CancellationToken ct)
{
// 1. 取得上游壓縮串流
await using Stream srcStream = await _storage.OpenReadAsync(objectKey, ct);
// 2. 設定回應標頭
string fileName = Path.GetFileNameWithoutExtension(objectKey); // 移除 .gz
Response.Headers.ContentDisposition = $"attachment; filename=\"{fileName}\"";
Response.Headers.ContentType = "application/octet-stream";
// 3. 包裝解壓縮串流 → 直接複製到 HTTP 回應 Body
await using var gzIn = new GZipInputStream(srcStream);
await gzIn.CopyToAsync(Response.Body, 81920, ct);
}
此模式適用於任何 Stream 來源:FileStream、MemoryStream、S3Object.ResponseStream、BlobClient.OpenRead()。
[HttpGet("download-zip")]
public async Task DownloadMultipleAsZip([FromQuery] string[] keys, CancellationToken ct)
{
Response.Headers.ContentDisposition = "attachment; filename=\"batch.zip\"";
Response.Headers.ContentType = "application/zip";
await using var zipOut = new ZipOutputStream(Response.Body) { IsStreamOwner = false };
zipOut.SetLevel(3); // 快速壓縮,降低 CPU
foreach (string key in keys)
{
// 逐個讀取物件並寫入 Zip Entry
await using Stream objStream = await _storage.OpenReadAsync(key, ct);
var entry = new ZipEntry(Path.GetFileName(key))
{
DateTime = DateTime.UtcNow,
Size = objStream.CanSeek ? objStream.Length : -1
};
zipOut.PutNextEntry(entry);
await objStream.CopyToAsync(zipOut, 81920, ct);
zipOut.CloseEntry();
}
await zipOut.FinishAsync(ct); // 寫入中央目錄結構
}
優勢:記憶體佔用恆定 ≈ 緩衝區大小 × 併發數,無論打包多少 GB 檔案都不會 OOM。
public async IAsyncEnumerable CreateZipEntriesAsync(
IEnumerable keys,
[EnumeratorCancellation] CancellationToken ct)
{
foreach (string key in keys)
{
ct.ThrowIfCancellationRequested();
await using Stream s = await _storage.OpenReadAsync(key, ct);
yield return new ZipEntry(Path.GetFileName(key))
{
DateTime = DateTime.UtcNow,
Size = s.CanSeek ? s.Length : -1
};
// 注意:這裡只產生 Entry metadata,實際資料複製在呼叫端
}
}
// 呼叫端
await using var zipOut = new ZipOutputStream(Response.Body) { IsStreamOwner = false };
await foreach (var entry in CreateZipEntriesAsync(keys, ct))
{
zipOut.PutNextEntry(entry);
await using Stream s = await _storage.OpenReadAsync(entry.Name, ct);
await s.CopyToAsync(zipOut, 81920, ct);
zipOut.CloseEntry();
}
await zipOut.FinishAsync(ct);
| 項目 | 最佳實踐 |
|---|---|
| 緩衝區 | 81920 bytes (80KB),避免 LOH、減少系統呼叫 |
| IsStreamOwner | 外層串流由呼叫端管理 → false;內層暫存串流 → true |
| Flush/Finish | GZipOutputStream.FlushAsync、ZipOutputStream.FinishAsync 必呼叫 |
| CancellationToken | 全程透傳,支援客戶端中斷釋放資源 |
| Seek 不支援 | 物件儲存串流通常不可 Seek,ZipEntry.Size 設 -1,SharpZipLib 自動處理 |
| 大檔案 Zip64 | zipOut.UseZip64 = UseZip64.On 支援 4GB+ Entry |
| 例外處理 | 包裝 try/finally 確保串流 Dispose,避免連線洩漏 |
| 庫 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| SharpZipLib | 零相依、支援 Stream、Zip/GZip/BZip2/Tar、.NET Standard 2.0 | API 較舊、文件稀少 | 跨平台、容器化、輕量微服務 |
| System.IO.Compression | 內建、ZipArchive 支援 Stream | 無 BZip2/Tar、無 AES 加密(舊版)、ZipArchive 需可 Seek 串流寫入中央目錄 | 簡單 Zip、.NET 6+ 專案 |
| ZstdSharp / Brotli | 壓縮率/速度更優 | 非標準格式、需雙端支援 | 內部高效能傳輸、日誌壓縮 |
掌握「串流包裝串流、CopyToAsync 穿透、IsStreamOwner 控制權」三大核心模式,即能在任何 .NET 服務中構建高效能、零暫存檔、可水平擴展的線上壓縮解壓縮管線,滿足現代雲原生架構的嚴苛資源限制。