在 .NET 生態中,SharpZipLib / System.IO.Compression 僅支援 ZIP 格式,若需原生支援 RAR、7z、分卷壓縮、加密密碼等高級功能,調用已安裝的 WinRAR(或 rar.exe/unrar.exe)是最穩健、相容性最佳的方案。本文提供兩套完整實作:
ZipOutputStream/ZipInputStream 處理 ZIP,無外部依賴。using System;
using System.IO;
using ICSharpCode.SharpZipLib.Zip; // NuGet: SharpZipLib
public static class ZipHelper
{
/// 壓縮整個目錄為 ZIP(保留相對路徑)
public static void CreateZip(string sourceDir, string zipPath, int level = 9, string password = null)
{
if (!Directory.Exists(sourceDir)) throw new DirectoryNotFoundException(sourceDir);
using (var fs = File.Create(zipPath))
using (var zip = new ZipOutputStream(fs))
{
zip.SetLevel(level);
if (!string.IsNullOrEmpty(password)) zip.Password = password;
byte[] buffer = new byte[4096];
foreach (string file in Directory.GetFiles(sourceDir, "*", SearchOption.AllDirectories))
{
string entryName = file.Substring(sourceDir.Length + 1).Replace('\\', '/');
var entry = new ZipEntry(entryName) { DateTime = DateTime.Now, Size = new FileInfo(file).Length };
zip.PutNextEntry(entry);
using (var fsIn = File.OpenRead(file))
{
int read;
while ((read = fsIn.Read(buffer, 0, buffer.Length)) > 0)
zip.Write(buffer, 0, read);
}
}
zip.Finish();
}
}
/// 解壓 ZIP 到指定目錄(自動建立子目錄)
public static void ExtractZip(string zipPath, string destDir, string password = null)
{
if (!File.Exists(zipPath)) throw new FileNotFoundException(zipPath);
Directory.CreateDirectory(destDir);
using (var fs = File.OpenRead(zipPath))
using (var zip = new ZipInputStream(fs))
{
if (!string.IsNullOrEmpty(password)) zip.Password = password;
ZipEntry entry;
while ((entry = zip.GetNextEntry()) != null)
{
string fullPath = Path.Combine(destDir, entry.Name);
if (entry.IsDirectory)
{
Directory.CreateDirectory(fullPath);
continue;
}
Directory.CreateDirectory(Path.GetDirectoryName(fullPath));
using (var outFs = File.Create(fullPath))
{
byte[] buffer = new byte[4096];
int read;
while ((read = zip.Read(buffer, 0, buffer.Length)) > 0)
outFs.Write(buffer, 0, read);
}
}
}
}
}
using System;
using System.Diagnostics;
using System.IO;
using Microsoft.Win32;
public static class WinRarHelper
{
/// 自動從註冊表獲取 WinRAR.exe 路徑,失敗回退常見安裝目錄
private static string GetWinRarPath()
{
using (var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\WinRAR.exe"))
{
if (key?.GetValue("") is string path && File.Exists(path)) return path;
}
// 常見回退路徑
string[] candidates = {
@"C:\Program Files\WinRAR\WinRAR.exe",
@"C:\Program Files (x86)\WinRAR\WinRAR.exe",
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "rar.exe") // 攜帶 rar.exe 部署
};
foreach (var p in candidates) if (File.Exists(p)) return p;
throw new FileNotFoundException("找不到 WinRAR.exe,請安裝或將 rar.exe 放在程式目錄。");
}
/// 通用執行器:隱藏視窗、同步等待、拋出異常
private static void Run(string arguments, string workingDir = null)
{
var exe = GetWinRarPath();
var psi = new ProcessStartInfo(exe, arguments)
{
UseShellExecute = false,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden,
WorkingDirectory = workingDir ?? Path.GetDirectoryName(exe)
};
using (var proc = Process.Start(psi))
{
proc.WaitForExit();
if (proc.ExitCode != 0)
throw new InvalidOperationException($"WinRAR 退出碼 {proc.ExitCode},參數:{arguments}");
}
}
// ========== 壓縮 ==========
/// 壓縮目錄/文件為 RAR(支援空格路徑)
/// 要壓縮的來源路徑(文件或目錄)
/// 輸出目錄
/// 輸出檔名(含副檔名 .rar/.zip)
public static void Compress(string inputPath, string outputDir, string archiveName)
{
if (!Directory.Exists(outputDir)) Directory.CreateDirectory(outputDir);
if (!File.Exists(inputPath) && !Directory.Exists(inputPath))
throw new ArgumentException("來源路徑不存在:" + inputPath);
// -r 遞歸 -ep1 排除基礎路徑 -ibck 後台模式 -y 覆蓋確認
string args = $"a -r -ep1 -ibck -y \"{Path.Combine(outputDir, archiveName)}\" \"{inputPath}\"";
Run(args);
}
/// 壓縮為 ZIP(WinRAR 也支援 ZIP 格式)
public static void CompressZip(string inputPath, string outputDir, string archiveName)
{
string args = $"a -afzip -r -ep1 -ibck -y \"{Path.Combine(outputDir, archiveName)}\" \"{inputPath}\"";
Run(args);
}
// ========== 解壓 ==========
/// 解壓縮包到指定目錄(支援 .rar/.zip/.7z 等 WinRAR 支援格式)
/// 壓縮包完整路徑
/// 解壓目標目錄(需預先存在)
public static void Extract(string archivePath, string destDir)
{
if (!File.Exists(archivePath)) throw new FileNotFoundException(archivePath);
if (!Directory.Exists(destDir)) Directory.CreateDirectory(destDir);
// x 保留路徑解壓 -o+ 覆蓋 -y 全是 -inul 禁用錯誤對話框
string args = $"x -inul -o+ -y \"{archivePath}\" \"{destDir}\"";
Run(args, Path.GetDirectoryName(archivePath));
}
/// 解壓到當前目錄(同名資料夾)
public static void ExtractHere(string archivePath) =>
Extract(archivePath, Path.GetDirectoryName(archivePath));
}
class Program
{
static void Main()
{
string work = @"D:\Projects\Demo";
string rarOut = @"D:\Output";
// 1. 壓縮整個資料夾為 RAR
WinRarHelper.Compress(work, rarOut, "Demo.rar");
// 2. 壓縮為 ZIP(相容性更好)
WinRarHelper.CompressZip(work, rarOut, "Demo.zip");
// 3. 解壓
WinRarHelper.Extract(@"D:\Output\Demo.rar", @"D:\Extract\Demo");
Console.WriteLine("完成");
}
}
| 參數 | 說明 |
|---|---|
| a | 添加文件到壓縮包 |
| x | 解壓並保留完整路徑 |
| e | 解壓但忽略路徑(所有文件平鋪) |
| -r | 遞歸子目錄 |
| -ep1 | 排除基礎目錄名(只存相對路徑) |
| -ep2 | 存完整路徑(含磁碟代號) |
| -afzip | 建立 ZIP 格式(預設 RAR) |
| -hp[pwd] | 加密並設定密碼(如 -hp123) |
| -ibck | 後台模式(無進度條) |
| -y | 所有提示預設 Yes |
| -o+ | 覆蓋現有文件不提示 |
| -inul | 禁用錯誤對話框(靜默模式) |
| -v | 分卷大小(如 -v100m) |
| -rr[N] | 添加恢復記錄 |
"path with space",代碼中已處理。WorkingDirectory 設為壓縮包所在目錄,相對路徑才正確。WaitForExit() 改用 proc.Exited 事件或 Task.Run 包裝。rar.exe + unrar.dll(約 1MB),免安裝 WinRAR 也能用,注意授權條款。純 .NET 方案適合純 ZIP、跨平台、無外部依賴場景;WinRAR 方案勝在格式支援全(RAR/7Z/分卷/加密/恢復記錄)、參數豐富、企業級穩定。本文提供的 WinRarHelper 封裝了註冊表定位、空格路徑、同步等待、錯誤碼檢查,開箱即用。根據專案需求選擇:單純 ZIP 用 SharpZipLib,需 RAR/高級功能用 WinRAR,兩者互補,構建完整壓縮體系。