[ASP.net WebForm] 使用 DotNetZip 套件壓縮/解壓縮 .zip 檔

  • 898
  • 0
  • C#
  • 2023-05-25

C# Compression using DotNetZip(Ionic.Zip)

前言

DotNetZip 這個壓縮/解壓縮.zip檔的套件,使用上很簡潔直覺易懂 

以下記錄此套件的使用方式

內文
using Ionic.Zip;//Nuget套件先找「DotNetZip」安裝後再引用此命名空間
using System;
using System.Collections.Generic;
using System.IO;

namespace MyWeb.Utility
{
    public class MyFileHelper
    {
        /// <summary>
        /// 將多個檔案打包成zip
        /// </summary>
        /// <param name="dFilePaths">Key為檔案總管完全路徑,Value為可讀性檔名</param>
        /// <param name="zipPWD"></param>
        /// <returns></returns>
        public static byte[] CompressFilesToZipArchive(string zipFileName, Dictionary<string, string> dFilePaths, string zipPWD = null)
        {
            //回傳結果
            byte[] file = null;
            //要回傳的.zip檔案串流
            using (MemoryStream ms = new MemoryStream())
            {
                //把多個檔案打包成一個.zip檔
                using (ZipFile zip = new ZipFile())
                {
                    //先設定編碼,避免壓縮檔裡的中文檔名為亂碼
                    zip.AlternateEncoding = System.Text.Encoding.UTF8;
                    zip.AlternateEncodingUsage = ZipOption.AsNecessary;
                    zip.Password = zipPWD;//zip上密碼,null值表示無密碼
                    zip.UseZip64WhenSaving = Zip64Option.AsNecessary;//避免壓縮大檔案出錯
                    //↓壓縮等級(視自己需求調整),沒寫就是預設值(平衡於壓縮速度和壓縮率)
                    //zip.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression;

                    //zipFileName當作zip檔內的目錄名稱
                    string folderNameInArchive = Path.GetFileNameWithoutExtension(zipFileName);
                    //將多筆檔案打包成.zip
                    foreach (KeyValuePair<string, string> entry in dFilePaths)
                    {
                        string sysfilePath = entry.Key;//來源檔案總管完全路徑
                        string zipEntryFileName = entry.Value;//在.zip檔內的可讀性檔名(不包含路徑)

                        #region 將sysFilePath轉成byte[]
                        FileStream fs = new FileStream(sysfilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                        BinaryReader br = new BinaryReader(fs,System.Text.Encoding.UTF8);
                        //讀取來源檔案
                        byte[] sourceFile = br.ReadBytes(Convert.ToInt32(fs.Length));
                        br.Close();fs.Close();//關閉串流
                        #endregion

                        //把來源檔案放到.zip檔內指定的目錄下(留意zipEntryFileName若重覆檔名在zip內的同一目錄下,程式會報錯)
                        zip.AddEntry(Path.Combine(folderNameInArchive, zipEntryFileName), sourceFile);
                    }//end foreach
                     
                    zip.Save(ms);//zip檔儲存在記憶體中 
                }//end using Zip
                file = ms.ToArray();
            }//end using MemoryStream
            return file;
            
        }//end method

        /// <summary>
        /// 解壓縮.zip檔
        /// </summary>
        /// <param name="zipFilePath"></param>
        /// <param name="extractToDir"></param>
        /// <param name="zipPWD"></param>
        public static void DeCompressZip(string zipFilePath, string extractToDir, string zipPWD = null)
        {
            if (Directory.Exists(extractToDir) == false)//解壓目錄不存在
            {
                Directory.CreateDirectory(extractToDir);
            }
            //解壓縮全部
            using (ZipFile zip = ZipFile.Read(zipFilePath))
            {
                zip.Password = zipPWD;//zip檔的解壓縮密碼,null值表示無密碼
                zip.ExtractAll(extractToDir);//解壓縮到指定的目錄
            }//end using
        }//end method

        /// <summary>
        /// 輸出(下載)檔案給前端
        /// </summary>
        /// <param name="response"></param>
        /// <param name="downloadFileName">下載檔名</param>
        /// <param name="file">要下載的二進位檔案</param>
        /// <param name="contentType">MIME-TYPE</param>
        public static void DownloadFileToClient(System.Web.HttpResponse response, byte[] downloadFile, string downloadFileName, string contentType)
        {
            //準備下載(以下如果沒完整輸出,7-Zip軟體解壓縮時會發生錯誤) 
            response.Clear();
            //跳出視窗,讓用戶端選擇要儲存的位置(使用 Uri.EscapeDataString() 編碼中文字才不會下載時,檔名為亂碼)
            response.AddHeader("Content-Disposition", "attachment; filename=" + Uri.EscapeDataString(downloadFileName));//給User看的下載檔名
            response.AddHeader("Content-Length", downloadFile.Length.ToString());
            response.ContentType = contentType;
            response.Flush();
            response.BinaryWrite(downloadFile);//檔案輸出下載
            response.End();
        }
    }//end class
}
前端叫用方式
using MyWeb.Utility;
using System;
using System.Collections.Generic;

namespace MyWeb
{
    public partial class zipForm : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        { 
        } 
        /// <summary>
        /// user點擊按鈕下載檔案
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void btnGo_Click(object sender, EventArgs e)
        {

            //要被打包壓縮的多個來源檔案路徑
            Dictionary<string, string> sourceFilePaths = new Dictionary<string, string>();
            /*
              實務上Dictionary的Key值有可能是系統編碼過的檔名(為了存放在Server上檔名不重覆)
              Value值為用戶上傳檔案的原始檔名
            */
            sourceFilePaths.Add(Server.MapPath("~/zipTestFolder/中文檔名0001.jpg"), "中文檔名0001.jpg");
            sourceFilePaths.Add(Server.MapPath("~/zipTestFolder/中文檔名0002.jpg"), "中文檔名0002.jpg");
            string zipFileName = "壓縮檔名.zip";
            
            try
            {
                //產出.zip二進位檔案
                byte[] zip = MyFileHelper.CompressFilesToZipArchive(zipFileName, sourceFilePaths);
                //輸出檔案給前端
                MyFileHelper.DownloadFileToClient(Response, zip, zipFileName, "application/zip");
                return;//流程不往下執行
            }
            catch (Exception ex)
            {//發生錯誤
                Response.Write(ex.ToString());
            }
           
        }//end btnGo_Click Event
    }
}
補充

ASP.net WebForm 的 Response 輸出檔案,如果沒完整輸出,7-Zip軟體解壓縮時會發生以下錯誤:
「有效負載盡頭外還有其他資料」、「There are some data after the end of the payload data」

 

參考文章

官方說明文件:AddFile Method (fileName, directoryPathInArchive)