日前遇到了一個無法使用 GZipStream / DeflateStream 解壓縮的zip檔,但用 7-zip 能正常的解壓縮;在N年前我就比較過7-zip與WinRAR的壓縮能力,從那時候起我就只用7-zip,WinRAR 就再也沒出現在我的環境裡。這次遇到.NET預設元件無法處理的問題,我第一個想到的是7-zip,使用 google 搜尋 後,我找到了SevenZipSharp ,它是由國外高手所寫的.NET元件,主要是用來調用核心7z.dll,7z.dll 在 7-zip 完畢後,就會在C:\Program Files\7-Zip 目錄裡出現。
為了讓團隊成員方便使用,我打算再將它重寫,並且把 7z.dll 加入我的專案裡,SevenZipSharp.dll 加入專案參考。
PS.7-zip 有x86與x64版本之分,我在單元測試時碰到了一個很大的麻煩,導致我無法使用這元件測試,下篇再來分享我所遇到的問題。
程式開始前:
當我們將SevenZipSharp.dll 加入參考後,可針對SevenZipCompressor class(壓縮) / SevenZipExtractor class(解壓縮) 開始 Survey ,這兩個類別都有個靜態方法 SetLibraryPath,它是用來指定7z.dll的路逕,若SevenZipSharp.dll 與7z.dll目錄相同便可省略這句。
調用 ExtractArchive 方法,解壓縮檔案到目錄。
public void DecompressionToFolder(string SourceFileName, string TargetDirectory) { using (SevenZipExtractor zip = new SevenZipExtractor(SourceFileName)) { zip.ExtractArchive(TargetDirectory); } }
調用 ExtractFile 方法,解壓縮檔案到實體檔案位置。
public void Decompression(string SourceFileName, string TargetFileName) { using (FileStream stream = new FileStream(TargetFileName, FileMode.Create, FileAccess.Write)) { this.Decompression(SourceFileName, stream); } } public void Decompression(string SourceFileName, Stream TargetStream) { SevenZipExtractor.SetLibraryPath(@"C:\Program Files\7-Zip\7z.dll");//若SevenZipSharp.dll 與7z.dll目錄相同便可省略這句 using (SevenZipExtractor zip = new SevenZipExtractor(SourceFileName)) { try { zip.ExtractFile(0, TargetStream); } finally { if (TargetStream != null) { TargetStream.Close(); } } } }
調用 ExtractFile 方法,解壓縮檔案到記憶體裡,這是我目前專案裡最需要的一個功能。
public Stream Decompression(string SourceFileName) { using (FileStream fileStream = new FileStream(SourceFileName, FileMode.Open, FileAccess.Read)) { return Decompression(fileStream); } } public Stream Decompression(Stream SourceStream) { using (SevenZipExtractor zip = new SevenZipExtractor(SourceStream)) { byte[] dataByteArray = new byte[SourceStream.Length]; MemoryStream targetStream = new MemoryStream(dataByteArray, true); zip.ExtractFile(0, targetStream); targetStream.Position = 0; return targetStream; } }
所以我的測試程式這麼寫,解壓縮到MemoryStream裡,然後讀取 Stream,由下列測試程式碼可見,Stream是一個文字檔。
/// <summary> /// A test for Decompression /// </summary> [DeploymentItem("7z.dll"), DeploymentItem("temp.txt.zip"), TestMethod()] public void 解壓縮測試_到memory() { SevenZipFactory target = new SevenZipFactory();
string SourceFileName = "temp.txt.zip"; if (!File.Exists(SourceFileName)) { throw new ArgumentNullException(); } var expected = "\"收訊者姓名\",\"收訊者門號\",\"發送內容\",\"收訊狀態\",\"收訊時間\",\"回覆時間\",\"回覆內容\"\r\n\"\",\"+886956778187\",\"預約發送測試\",\"發送成功\",\"2012/04/26 下午 02:56:24\",\"\",\"\"\r\n\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; var fileStream = target.Decompression(SourceFileName); StreamReader reader = new StreamReader(fileStream, Encoding.UTF8); var actual = reader.ReadToEnd(); Assert.AreEqual(actual, expected); }
補充:
原本的解壓縮會造成有些檔案無法解壓縮,經 demo 指點改成以下
public Stream Decompression(Stream SourceStream) { using (SevenZipExtractor zip = new SevenZipExtractor(SourceStream)) { MemoryStream targetStream = new MemoryStream(); zip.ExtractFile(0, targetStream); targetStream.Position = 0; return targetStream; } }