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

C# 壓縮解壓縮完整實作:System.IO.Compression 與 SharpZipLib 兩大方案

     編輯:ASP.NET 基礎

前言

C# 專案中處理壓縮解壓縮,.NET Framework 4.5+ 內建 `System.IO.Compression` 支援 ZIP/GZIP,功能足夠大多數場景;若需支援 RAR、7z、密碼保護、分卷壓縮等進階功能,則需第三方庫 `SharpZipLib` 或 `SevenZipSharp`。本文提供三種層級的完整實作。

方案一:.NET 內建 - 簡單單檔 GZIP/Deflate

適用於單一檔案串流壓縮,無資料夾結構概念。

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 內建 ZIP - 資料夾壓縮/解壓縮 (推薦標準方案)

.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;
    }
}

方案三:SharpZipLib - 進階功能 (密碼、事件、串流、多格式)

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

常見坑點與解決

  1. 中文檔名亂碼:.NET 4.5+ `ZipFile` 預設 UTF-8,SharpZipLib 需 `zipStream.UseUnicodeText = true`
  2. 大檔案 OutOfMemory:避免 `File.ReadAllBytes`,使用 `CopyTo` 或分塊讀寫
  3. 資料夾權限:壓縮/解壓目錄需 IIS_IUSRS 讀寫權限
  4. 路徑長度限制:啟用 `UseZip64`、長路徑前綴 `\\?\` (需 .NET 4.6.2+ 且啟用 `LongPathsEnabled`)
  5. 壓縮正在使用的檔案:`FileShare.ReadWrite` 開啟,或先複製到暫存區

總結

現代 C# 專案首選 `System.IO.Compression.ZipFile`,零相依、標準相容、效能優異。僅當需要 密碼保護、進度回呼、非 ZIP 格式 時,引入 SharpZipLib 或 SevenZipSharp。封裝為 `ICompressor` 介面可輕鬆切換實作,符合開放封閉原則。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved