如何使用 .NET Framework 內建的 zip 壓縮功能 (二)

  • 1055
  • 0

上一次,筆者介紹了如何使用.NET Framework 內建的 Zip 壓縮功能,但上次只提到如何壓縮檔案,未提到如何解壓縮檔案

前言

上一次,筆者介紹了「如何使用 .NET Framework 內建的 zip 壓縮功能」,但上次只提到如何壓縮檔案,未提到如何解壓縮檔案,今天筆者就再來補足如何使用內建 ZipArchive 解壓縮檔案。

在前一篇文章中,筆者說明了 ZipArchive 類別的用法,以及 ZipArchiveModel 提供的三種設定方式,現在,我們要解壓縮檔案只要使用第 2. Read 方式即可。由於程式碼中使用的順序性幾乎都相同,而且前篇文章中已經說明得相當清楚,所以這邊筆者就使用一張「壓縮檔案」與「解壓縮檔案」的比較圖給讀者來參考,如下:

從上圖我們可以輕易的瞭解壓縮與解壓縮處理的方式的不同之處,也可以發現程序其實是相同的,

第一個、FileStream 改變為「開啟Zip檔案」

第二個、原本是 CreateNew 的 ZipArchive 變成 Read 方式

第三個、原本是在現有 ZipArchive 中 Create 一個 ZipArchive 壓縮檔案則改變為讀取所有 Entries

第四個、原本是建立 ZipEntry 改變為讀取現有 Zip 檔案中要解壓縮的那個檔案的 Entry

第五個、原本是將 byte[] 資訊寫入 ZipEntry 中,改變為從 ZipEntry 讀取出來的 stream 轉成 byte[] 後寫入實體檔案 (解壓縮)

 

所謂的 ZipEntry 是表示在Zip檔案中,可以識別一個檔案的單位,每一個檔案即為一個 Entry

 

另外,如果您需要一次解壓縮多個檔案,您可以在取得所有 Entries 的地方 「archive.Entries 」(上方第三個步驟)改以回圈方式,並重複「第四個」、「第五個」步驟就可以將 Entries 內所有檔案解壓縮出來。

第一篇文章加上第二篇文章所有完整的程式碼如下(使用 LINQPad):

public class ZipArchiveFile
{
	/// 建立壓縮檔案
	/// </summary>
	/// <param name="sourceFile">來源 Excel 檔案</param>
	/// <param name="targetFile">目標 Zip 檔案</param>
	/// <returns>The target Zip FileName</returns>
	public static string CreateZipArchiveByExcel(string sourceFile)
	{
		bool result = false;
		string workPath = Path.GetDirectoryName(sourceFile);
		string createEntryFileName = Path.GetFileName(sourceFile);
		string targetZipFileName = string.Format("{0}{1}", Path.GetFileNameWithoutExtension(sourceFile), ".zip");
		string distinationFile = Path.Combine(workPath, targetZipFileName);

		FileStream f = new FileStream(sourceFile, FileMode.Open, FileAccess.Read);

		try
		{
			using (var fileStream = new FileStream(distinationFile, FileMode.CreateNew))
			{
				using (var archive = new ZipArchive(fileStream, ZipArchiveMode.Create, true))
				{
					byte[] xlsxBytes = BinaryUtil.BinaryReadToEnd(f);

					var zipArchiveEntry = archive.CreateEntry(createEntryFileName, CompressionLevel.Fastest);

					using (var zipStream = zipArchiveEntry.Open())
					{
						zipStream.Write(xlsxBytes, 0, xlsxBytes.Length);
					}
					result = true;
				}
			}
		}
		finally
		{
			f.Close();
		}

		return distinationFile;
	}
	
	public static string GetFileFromZipArchive(string sourceFile, string extractEntryFileName)
	{
		string workPath = Path.GetDirectoryName(sourceFile);
		string distinationFile = Path.Combine(workPath, contentEntryFileName);

		FileStream f = new FileStream(distinationFile, FileMode.CreateNew, FileAccess.ReadWrite);

		try
		{
			using (var fileStream = new FileStream(sourceFile, FileMode.Open))
			{
				using (var archive = new ZipArchive(fileStream, ZipArchiveMode.Read, true))
				{					
					var zipArchiveEntry = archive.Entries
						.Where(c => c.Name == extractEntryFileName)
						.FirstOrDefault(); 
					
					if(zipArchiveEntry == null)
						return;
						
					using (var zipStream = zipArchiveEntry.Open())
					{
						BinaryWriter bw = new BinaryWriter(f);
						byte[] xlsxBytes = BinaryUtil.BinaryReadToEnd(zipStream);
						bw.Write(xlsxBytes);
						bw.Close();
					}
				}
			}
		}
		finally
		{
			f.Close();
		}

		return distinationFile;
	}
}

class BinaryUtil
{
	public static byte[] BinaryReadToEnd(Stream stream)
	{
		long originalPosition = 0;
	
		if (stream.CanSeek)
		{
			originalPosition = stream.Position;
			stream.Position = 0;
		}
	
		try
		{
			byte[] readBuffer = new byte[4096];
	
			int totalBytesRead = 0;
			int bytesRead;
	
			while ((bytesRead = stream.Read(readBuffer, totalBytesRead, readBuffer.Length - totalBytesRead)) > 0)
			{
				totalBytesRead += bytesRead;
	
				if (totalBytesRead == readBuffer.Length)
				{
					int nextByte = stream.ReadByte();
					if (nextByte != -1)
					{
						byte[] temp = new byte[readBuffer.Length * 2];
						Buffer.BlockCopy(readBuffer, 0, temp, 0, readBuffer.Length);
						Buffer.SetByte(temp, totalBytesRead, (byte)nextByte);
						readBuffer = temp;
						totalBytesRead++;
					}
				}
			}
	
			byte[] buffer = readBuffer;
			if (readBuffer.Length != totalBytesRead)
			{
				buffer = new byte[totalBytesRead];
				Buffer.BlockCopy(readBuffer, 0, buffer, 0, totalBytesRead);
			}
			return buffer;
		}
		finally
		{
			if (stream.CanSeek)
			{
				stream.Position = originalPosition;
			}
		}
	}
}

void Main()
{
	string sourceFile = @"D:\Gelis Documents\客戶提供資料\Summary.zip";
	string extractFileName = ZipArchiveFile.GetFileFromZipArchive(sourceFile, "Summary.xlsx");
}

所以,我只要使用上面 「Main」中的程式碼,即可將 Summary.zip 中的 Summary.xlsx 解壓縮出來。

 

結語:

下一次讀者在 .net 的程式中如需要 Zip 壓縮與解壓縮功能時,可以試試看 ZipArchive,完全不需要安裝任何套件,而且壓縮與解壓縮效率極快,不失為一個好選擇喔。


 

簽名:

學習是一趟奇妙的旅程

這當中,有辛苦、有心酸、也有成果。有時也會有瓶頸。要能夠繼續勇往直前就必須保有一顆最熱誠的心。

軟體開發之路(FB 社團)https://www.facebook.com/groups/361804473860062/

Gelis 程式設計訓練營(粉絲團)https://www.facebook.com/gelis.dev.learning/


 

如果文章對您有用,幫我點一下讚,或是點一下『我要推薦,這會讓我更有動力的為各位讀者撰寫下一篇文章。

非常謝謝各位的支持與愛護,小弟在此位各位說聲謝謝!!! ^_^