首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# SharpZipLib 壓縮解壓縮實戰:ZipClass 與 UnZipClass 封裝

     編輯:關於C#

前言

專案需求:將任意檔案/目錄壓縮為 ZIP,或解壓縮 ZIP 至指定資料夾。選用開源 **ICSharpCode.SharpZipLib**,僅需引用單一 DLL,無外部相依,適合 .NET Framework / .NET Core 專案。

NuGet 安裝

dotnet add package SharpZipLib
// 或
Install-Package SharpZipLib

壓縮類 ZipHelper (整合原文 ZipClass/ZipFileMain)


using System;
using System.IO;
using ICSharpCode.SharpZipLib.Zip;
using ICSharpCode.SharpZipLib.Checksums;

public static class ZipHelper
{
    /// 單一檔案壓縮
    public static void CompressFile(string sourceFile, string destZip, int compressionLevel = 6, int bufferSize = 4096)
    {
        if (!File.Exists(sourceFile)) throw new FileNotFoundException("來源檔案不存在", sourceFile);

        using (var fsIn = File.OpenRead(sourceFile))
        using (var fsOut = File.Create(destZip))
        using (var zipStream = new ZipOutputStream(fsOut))
        {
            zipStream.SetLevel(compressionLevel); // 0=Store, 9=BestCompression
            var entry = new ZipEntry(Path.GetFileName(sourceFile)) { DateTime = DateTime.Now };
            zipStream.PutNextEntry(entry);

            byte[] buffer = new byte[bufferSize];
            int read;
            while ((read = fsIn.Read(buffer, 0, buffer.Length)) > 0)
                zipStream.Write(buffer, 0, read);
        }
    }

    /// 目錄壓縮 (保留相對路徑)
    public static void CompressDirectory(string sourceDir, string destZip, int compressionLevel = 6, string password = null)
    {
        if (!Directory.Exists(sourceDir)) throw new DirectoryNotFoundException("來源目錄不存在");

        using (var fsOut = File.Create(destZip))
        using (var zipStream = new ZipOutputStream(fsOut))
        {
            zipStream.SetLevel(compressionLevel);
            if (!string.IsNullOrEmpty(password)) zipStream.Password = password;

            var files = Directory.GetFiles(sourceDir, "*", SearchOption.AllDirectories);
            foreach (var file in files)
            {
                var relPath = file.Substring(sourceDir.Length).TrimStart(Path.DirectorySeparatorChar);
                var entry = new ZipEntry(relPath) { DateTime = File.GetLastWriteTime(file) };
                zipStream.PutNextEntry(entry);

                using (var fsIn = File.OpenRead(file))
                {
                    byte[] buffer = new byte[4096];
                    int read;
                    while ((read = fsIn.Read(buffer, 0, buffer.Length)) > 0)
                        zipStream.Write(buffer, 0, read);
                }
            }
        }
    }

    /// 進階:自訂 CRC/Size 寫入 Header (相容某些解壓工具)
    public static void CompressWithHeaderInfo(string sourceFile, string destZip)
    {
        using (var fsIn = File.OpenRead(sourceFile))
        using (var fsOut = File.Create(destZip))
        using (var zipStream = new ZipOutputStream(fsOut))
        {
            zipStream.SetLevel(6);
            var crc = new Crc32();
            byte[] buffer = new byte[fsIn.Length];
            fsIn.Read(buffer, 0, buffer.Length);
            crc.Reset(); crc.Update(buffer);

            var entry = new ZipEntry(Path.GetFileName(sourceFile))
            {
                DateTime = DateTime.Now,
                Size = fsIn.Length,
                Crc = crc.Value
            };
            zipStream.PutNextEntry(entry);
            zipStream.Write(buffer, 0, buffer.Length);
        }
    }
}

解壓縮類 UnZipHelper


using System;
using System.IO;
using ICSharpCode.SharpZipLib.Zip;

public static class UnZipHelper
{
    /// 解壓縮 ZIP 至目標目錄
    public static void Extract(string zipFile, string destDir, string password = null, bool overwrite = true)
    {
        if (!File.Exists(zipFile)) throw new FileNotFoundException("ZIP 檔案不存在", zipFile);
        Directory.CreateDirectory(destDir);

        using (var fs = File.OpenRead(zipFile))
        using (var zipStream = new ZipInputStream(fs))
        {
            if (!string.IsNullOrEmpty(password)) zipStream.Password = password;

            ZipEntry entry;
            while ((entry = zipStream.GetNextEntry()) != null)
            {
                if (entry.IsDirectory) continue; // 目錄由檔案建立時自動建立

                string targetPath = Path.Combine(destDir, entry.Name);
                string targetDir = Path.GetDirectoryName(targetPath);
                if (!string.IsNullOrEmpty(targetDir)) Directory.CreateDirectory(targetDir);

                if (File.Exists(targetPath) && !overwrite) continue;

                using (var fsOut = File.Create(targetPath))
                {
                    byte[] buffer = new byte[4096];
                    int read;
                    while ((read = zipStream.Read(buffer, 0, buffer.Length)) > 0)
                        fsOut.Write(buffer, 0, read);
                }
                File.SetLastWriteTime(targetPath, entry.DateTime);
            }
        }
    }

    /// 列舉 ZIP 內容 (不解壓)
    public static IEnumerable ListEntries(string zipFile)
    {
        using (var fs = File.OpenRead(zipFile))
        using (var zipFileObj = new ZipFile(fs))
        {
            foreach (ZipEntry entry in zipFileObj)
                yield return entry;
        }
    }
}

使用範例


// 壓縮單一檔案
ZipHelper.CompressFile(@"C:\Data\report.pdf", @"C:\Backup\report.zip", compressionLevel: 9);

// 壓縮整個資料夾 (含子目錄)
ZipHelper.CompressDirectory(@"C:\Projects\MyApp", @"C:\Backup\MyApp.zip", password: "secret123");

// 解壓縮
UnZipHelper.Extract(@"C:\Backup\MyApp.zip", @"C:\Restore\MyApp", password: "secret123");

// 僅列出內容
foreach (var e in UnZipHelper.ListEntries(@"C:\Backup\MyApp.zip"))
    Console.WriteLine($"{e.Name,-40} {e.Size,10} bytes  {e.DateTime:yyyy-MM-dd HH:mm}  CRC:{e.Crc:X8}");

常見坑與對策

問題原因解決
中文檔名亂碼SharpZipLib 預設用 UTF-8,舊版 Windows 解壓工具預期 Big5/CP950設定 zipStream.UseZip64 = UseZip64.Off 或改用 ZipFile 類並指定 Encoding
大於 4GB 檔案失敗傳統 ZIP 32-bit 限制啟用 UseZip64.On (預設自動),確保目標解壓工具支援 ZIP64
資料流未關閉導致檔案鎖定忘記 Dispose全程使用 using 區塊
密碼保護但仍可列出檔名ZIP 加密僅加密資料,不加密 Header若需隱藏檔名,先打包為無名 tar 再加密,或改用 7z/AES-256

效能調優建議

  • 緩衝區大小:4KB~64KB 視磁碟/SSD 特性調整,過大反而增加 LOH 壓力。
  • 平行壓縮:多檔案可用 Parallel.ForEach,但單一大檔無法平行 (ZIP 串流式)。
  • 記憶體限制:避免一次性 `ReadAllBytes` 大檔案,務必分塊串流讀寫。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved