[C#.NET] 使用 7-zip 解壓縮檔案
日前遇到了一個無法使用 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 方法,解壓縮檔案到目錄。
{
using (SevenZipExtractor zip = new SevenZipExtractor(SourceFileName))
{
zip.ExtractArchive(TargetDirectory);
}
}
調用 ExtractFile 方法,解壓縮檔案到實體檔案位置。
{
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 方法,解壓縮檔案到記憶體裡,這是我目前專案裡最需要的一個功能。
{
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是一個文字檔。
/// 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 指點改成以下
{
using (SevenZipExtractor zip = new SevenZipExtractor(SourceStream))
{
MemoryStream targetStream = new MemoryStream();
zip.ExtractFile(0, targetStream);
targetStream.Position = 0;
return targetStream;
}
}
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET