SharpZipLib (ICSharpCode.SharpZipLib) 是 .NET 生態系歷史最悠久、相依性最輕量的開源壓縮庫,支援 Zip、GZip、BZip2、Tar 等格式,相容 .NET Framework 2.0+ 至 .NET 8。本文以 Zip 為主,示範單檔/多檔壓縮解壓、AES 加密、ZIP 註解、壓縮等級控制,並修正原始碼中常見的緩衝區大小錯誤。
dotnet add package SharpZipLib
public enum CompressionAlgorithm { Zip, GZip, BZip2 }
public static class ZipHelper
{
// ... 以下方法皆為 static,方便直接呼叫
}
public static bool CompressFile(string srcPath, string destPath, CompressionAlgorithm algo)
{
try
{
using FileStream inFs = File.OpenRead(srcPath);
using FileStream outFs = File.Create(destPath);
switch (algo)
{
case CompressionAlgorithm.Zip:
// Zip 需建立 ZipOutputStream,單一 Entry
using (var zipOut = new ZipOutputStream(outFs))
{
zipOut.SetLevel(6); // 0-9
var entry = new ZipEntry(Path.GetFileName(srcPath))
{
DateTime = DateTime.Now,
Size = inFs.Length
};
zipOut.PutNextEntry(entry);
inFs.CopyTo(zipOut);
zipOut.CloseEntry();
}
break;
case CompressionAlgorithm.GZip:
using (var gzOut = new GZipOutputStream(outFs))
{
inFs.CopyTo(gzOut);
}
break;
case CompressionAlgorithm.BZip2:
// BZip2 需額外引用 ICSharpCode.SharpZipLib.BZip2
BZip2.Compress(inFs, outFs, true, 4096);
break;
}
return true;
}
catch { return false; }
}
public static bool DecompressFile(string srcPath, string destPath, CompressionAlgorithm algo)
{
try
{
using FileStream inFs = File.OpenRead(srcPath);
using FileStream outFs = File.Create(destPath);
switch (algo)
{
case CompressionAlgorithm.Zip:
using (var zipIn = new ZipInputStream(inFs))
{
ZipEntry entry = zipIn.GetNextEntry(); // 單檔取第一個
if (entry == null) return false;
zipIn.CopyTo(outFs);
}
break;
case CompressionAlgorithm.GZip:
using (var gzIn = new GZipInputStream(inFs))
gzIn.CopyTo(outFs);
break;
case CompressionAlgorithm.BZip2:
BZip2.Decompress(inFs, outFs, true);
break;
}
return true;
}
catch { return false; }
}
public static void CreateZipFromDirectory(
string sourceDir, string zipPath,
int compressionLevel = 6, // 0=Store, 1=BestSpeed ... 9=BestCompression
string password = null, // AES-256 (SharpZipLib 1.3+)
string comment = null, // ZIP 檔案註解
string searchPattern = "*", // 檔案過濾
SearchOption searchOption = SearchOption.AllDirectories)
{
using var zipOut = new ZipOutputStream(File.Create(zipPath))
{
Password = password, // null 表示不加密
UseZip64 = UseZip64.On, // 支援 4GB+ 大檔
};
zipOut.SetLevel(compressionLevel);
if (!string.IsNullOrEmpty(comment))
zipOut.SetComment(comment);
// 計算基準路徑長度,用於保留相對目錄結構
string baseDir = Path.GetFullPath(sourceDir).TrimEnd(Path.DirectorySeparatorChar);
int baseLen = baseDir.Length + 1; // +1 for separator
foreach (string file in Directory.GetFiles(sourceDir, searchPattern, searchOption))
{
string relativePath = file.Substring(baseLen).Replace(Path.DirectorySeparatorChar, '/');
var entry = new ZipEntry(relativePath)
{
DateTime = File.GetLastWriteTime(file),
Size = new FileInfo(file).Length
};
zipOut.PutNextEntry(entry);
using FileStream fs = File.OpenRead(file);
fs.CopyTo(zipOut); // .NET Core 2.1+ Stream.CopyTo 自動緩衝
zipOut.CloseEntry();
}
zipOut.IsStreamOwner = true; // 確保 Dispose 關閉底層 FileStream
}
public static void ExtractZipToDirectory(
string zipPath, string destDir,
string password = null,
bool overwrite = true)
{
Directory.CreateDirectory(destDir);
using var zipIn = new ZipInputStream(File.OpenRead(zipPath));
if (!string.IsNullOrEmpty(password))
zipIn.Password = password;
ZipEntry entry;
while ((entry = zipIn.GetNextEntry()) != null)
{
if (entry.IsDirectory) continue; // 目錄項目由檔案項目隱含建立
string destFile = Path.Combine(destDir, entry.Name);
// 防止 Zip Slip 攻擊:確保解壓路徑在目標目錄內
destFile = Path.GetFullPath(destFile);
if (!destFile.StartsWith(Path.GetFullPath(destDir), StringComparison.OrdinalIgnoreCase))
throw new SecurityException("Zip Slip 偵測:嘗試跳出目標目錄");
if (File.Exists(destFile) && !overwrite) continue;
Directory.CreateDirectory(Path.GetDirectoryName(destFile)!);
using FileStream outFs = File.Create(destFile);
zipIn.CopyTo(outFs);
}
}
| Level | 速度 | 壓縮率 | 建議場景 |
|---|---|---|---|
| 0 (Store) | 極快 | 無壓縮 | 已壓縮格式、需隨機存取 |
| 1-3 | 快 | 低 | 即時備份、網路傳輸 |
| 4-6 (預設 6) | 平衡 | 中 | 通用預設值 |
| 7-9 | 慢 | 高 | 離線封存、存儲成本敏感 |
惡意 Zip 可能包含 ../../etc/passwd 等路徑遍歷項目。ExtractZipToDirectory 中的 Path.GetFullPath + StartsWith 檢查為必要防禦。
Stream.CopyTo(內部 81920 bytes 緩衝),不將整個檔案讀入記憶體,支援 GB 級大檔。byte[] buffer = new byte[81920] 迴圈並回呼進度。// 壓縮整個資料夾,AES-256 加密,附註解
ZipHelper.CreateZipFromDirectory(
@"E:\Data\Export", @"E:\Backup\Export_20240518.zip",
compressionLevel: 6, password: "Str0ngP@ssw0rd!",
comment: "每日匯出備份 2024-05-18");
// 解壓縮
ZipHelper.ExtractZipToDirectory(
@"E:\Backup\Export_20240518.zip", @"E:\Restore\Export",
password: "Str0ngP@ssw0rd!");
SharpZipLib API 成熟穩定、零原生相依、支援 .NET Standard 2.0,是跨平台 .NET 專案處理 Zip 的首選輕量方案。掌握上述四大核心方法,即能覆蓋 95% 以上的壓縮/解壓縮業務需求。