C# 專案中處理壓縮解壓縮,.NET Framework 4.5+ 內建 `System.IO.Compression` 支援 ZIP/GZIP,功能足夠大多數場景;若需支援 RAR、7z、密碼保護、分卷壓縮等進階功能,則需第三方庫 `SharpZipLib` 或 `SevenZipSharp`。本文提供三種層級的完整實作。
適用於單一檔案串流壓縮,無資料夾結構概念。
using System.IO;
using System.IO.Compression;
public static class SimpleCompression
{
private const int BufferSize = 20480;
/// <summary>GZIP 解壓縮單一檔案</summary>
public static void DecompressGZip(string sourceFile, string destFile)
{
if (!File.Exists(sourceFile)) throw new FileNotFoundException("來源檔案不存在", sourceFile);
using (FileStream sourceStream = new FileStream(sourceFile, FileMode.Open))
using (GZipStream gzipStream = new GZipStream(sourceStream, CompressionMode.Decompress))
using (FileStream destStream = new FileStream(destFile, FileMode.Create))
{
gzipStream.CopyTo(destStream);
}
}
/// <summary>GZIP 壓縮單一檔案</summary>
public static void CompressGZip(string sourceFile, string destFile)
{
using (FileStream sourceStream = new FileStream(sourceFile, FileMode.Open))
using (FileStream destStream = new FileStream(destFile, FileMode.Create))
using (GZipStream gzipStream = new GZipStream(destStream, CompressionMode.Compress))
{
sourceStream.CopyTo(gzipStream);
}
}
}
.NET 4.5+ `ZipFile` / `ZipArchive` 類別,支援資料夾遞迴、相對路徑保留、UTF-8 檔名。
using System.IO;
using System.IO.Compression;
public static class ZipHelper
{
/// <summary>壓縮整個資料夾為 ZIP</summary>
/// <param name="sourceDir">來源資料夾路徑</param>
/// <param name="zipPath">輸出 .zip 檔路徑</param>
/// <param name="includeBaseFolder">是否包含最外層資料夾名稱</param>
public static void CreateZip(string sourceDir, string zipPath, bool includeBaseFolder = false)
{
if (File.Exists(zipPath)) File.Delete(zipPath);
if (includeBaseFolder)
{
// 簡單一行:包含資料夾本身
ZipFile.CreateFromDirectory(sourceDir, zipPath, CompressionLevel.Optimal, false);
}
else
{
// 進階:不包含最外層資料夾,只壓縮內容
using (var zip = ZipFile.Open(zipPath, ZipArchiveMode.Create))
{
var files = Directory.GetFiles(sourceDir, "*.*", SearchOption.AllDirectories);
string basePrefix = sourceDir.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
foreach (string file in files)
{
string entryName = file.Substring(basePrefix.Length).Replace('\\', '/');
zip.CreateEntryFromFile(file, entryName, CompressionLevel.Optimal);
}
}
}
}
/// <summary>解壓縮 ZIP 到指定資料夾</summary>
public static void ExtractZip(string zipPath, string destDir, bool overwrite = true)
{
if (!File.Exists(zipPath)) throw new FileNotFoundException("ZIP 檔案不存在", zipPath);
Directory.CreateDirectory(destDir);
// .NET 內建會自動處理目錄建立、覆寫
ZipFile.ExtractToDirectory(zipPath, destDir, overwrite);
}
/// <summary>列舉 ZIP 內容不解壓縮</summary>
public static List ListZipContents(string zipPath)
{
var result = new List();
using (var zip = ZipFile.OpenRead(zipPath))
{
foreach (var entry in zip.Entries)
{
result.Add($"{entry.FullName} ({entry.Length} bytes, 壓縮後 {entry.CompressedLength})");
}
}
return result;
}
}
NuGet: `Install-Package SharpZipLib`
using System;
using System.IO;
using ICSharpCode.SharpZipLib.Zip;
using ICSharpCode.SharpZipLib.Checksums;
public static class SharpZipLibHelper
{
/// <summary>壓縮資料夾 (支援密碼、進度事件、註解)</summary>
public static void CreateZip(string sourceDir, string zipPath, string password = null, Action progress = null)
{
using (var fs = File.Create(zipPath))
using (var zipStream = new ZipOutputStream(fs))
{
zipStream.SetLevel(6); // 0-9
zipStream.Password = password; // 加密
if (!string.IsNullOrEmpty(password))
zipStream.UseZip64 = UseZip64.On; // 密碼需 Zip64
CompressFolder(sourceDir, zipStream, sourceDir, progress);
zipStream.Finish();
}
}
private static void CompressFolder(string baseDir, ZipOutputStream zipStream, string rootDir, Action progress)
{
string[] entries = Directory.GetFileSystemEntries(baseDir);
foreach (string entry in entries)
{
if (Directory.Exists(entry))
{
CompressFolder(entry, zipStream, rootDir, progress);
}
else
{
CompressFile(entry, zipStream, rootDir, progress);
}
}
}
private static void CompressFile(string file, ZipOutputStream zipStream, string rootDir, Action progress)
{
using (FileStream fs = File.OpenRead(file))
{
string entryName = file.Substring(rootDir.Length).TrimStart(Path.DirectorySeparatorChar).Replace('\\', '/');
var entry = new ZipEntry(entryName) { DateTime = DateTime.Now, Size = fs.Length };
Crc32 crc = new Crc32();
byte[] buffer = new byte[fs.Length];
fs.Read(buffer, 0, buffer.Length);
crc.Reset();
crc.Update(buffer);
entry.Crc = crc.Value;
zipStream.PutNextEntry(entry);
long totalRead = 0;
int read;
while ((read = fs.Read(buffer, 0, buffer.Length)) > 0)
{
zipStream.Write(buffer, 0, read);
totalRead += read;
progress?.Invoke(entryName, totalRead, fs.Length);
}
}
}
/// <summary>解壓縮 ZIP (支援密碼、覆寫控制、進度)</summary>
public static void ExtractZip(string zipPath, string destDir, string password = null, bool overwrite = true, Action progress = null)
{
if (!File.Exists(zipPath)) throw new FileNotFoundException("ZIP 不存在", zipPath);
Directory.CreateDirectory(destDir);
using (var fs = File.OpenRead(zipPath))
using (var zipStream = new ZipInputStream(fs))
{
zipStream.Password = password;
ZipEntry entry;
while ((entry = zipStream.GetNextEntry()) != null)
{
if (entry.IsDirectory) continue;
string destFile = Path.Combine(destDir, entry.Name);
string destDirName = Path.GetDirectoryName(destFile);
if (!string.IsNullOrEmpty(destDirName))
Directory.CreateDirectory(destDirName);
if (File.Exists(destFile) && !overwrite) continue;
using (var fileStream = File.Create(destFile))
{
byte[] buffer = new byte[4096];
long totalRead = 0;
int read;
while ((read = zipStream.Read(buffer, 0, buffer.Length)) > 0)
{
fileStream.Write(buffer, 0, read);
totalRead += read;
progress?.Invoke(entry.Name, totalRead, entry.Size);
}
}
}
}
}
}
整合上述邏輯,提供統一介面:
public enum CompressionFormat { Zip, GZip, BZip2, Tar }
public interface ICompressor
{
void Compress(string source, string destination, CompressionOptions options = null);
void Decompress(string source, string destination, CompressionOptions options = null);
}
public class CompressionOptions
{
public int Level { get; set; } = 6; // 0-9
public string Password { get; set; }
public bool Overwrite { get; set; } = true;
public Action Progress { get; set; }
}
public class ZipCompressor : ICompressor
{
public void Compress(string source, string destination, CompressionOptions opt = null)
{
opt = opt ?? new CompressionOptions();
SharpZipLibHelper.CreateZip(source, destination, opt.Password, opt.Progress);
}
public void Decompress(string source, string destination, CompressionOptions opt = null)
{
opt = opt ?? new CompressionOptions();
SharpZipLibHelper.ExtractZip(source, destination, opt.Password, opt.Overwrite, opt.Progress);
}
}
// 使用範例
var compressor = new ZipCompressor();
compressor.Compress(@"D:\Data", @"D:\Backup\data.zip", new CompressionOptions
{
Level = 9,
Password = "secret123",
Progress = (name, done, total) => Console.WriteLine($"{name}: {done}/{total}")
});
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| System.IO.Compression (ZipFile) | 零相依、效能佳、UTF-8 檔名、標準 ZIP | 無密碼、無分卷、無進度事件 | 90% 一般專案、部署簡單優先 |
| SharpZipLib | 密碼、進度、註解、多格式、串流操作 | 需額外 DLL、效能稍低、API 較舊 | 需加密、大檔進度顯示、非標準格式 |
| SevenZipSharp (7-Zip) | 最高壓縮率、支援 7z/RAR/ISO、分卷 | 需 7z.dll、LGPL 授權、非純 managed | 極致壓縮、支援 RAR 解壓 |
| GZipStream/DeflateStream | 串流式、記憶體效率高 | 單檔、無封存結構 | HTTP 壓縮、日誌輪轉、資料庫 Blob |
現代 C# 專案首選 `System.IO.Compression.ZipFile`,零相依、標準相容、效能優異。僅當需要 密碼保護、進度回呼、非 ZIP 格式 時,引入 SharpZipLib 或 SevenZipSharp。封裝為 `ICompressor` 介面可輕鬆切換實作,符合開放封閉原則。