2013年9月10日 星期二

[轉貼] 使用 7-zip 解壓縮檔案

出處:http://www.dotblogs.com.tw/yc421206/archive/2012/04/30/71911.aspx

日前遇到了一個無法使用 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 目錄裡出現。
image
為了讓團隊成員方便使用,我打算再將它重寫,並且把 7z.dll 加入我的專案裡,SevenZipSharp.dll 加入專案參考。
image
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;
    }
}

沒有留言:

張貼留言