長久以來,我們在 C# 裡需要使用 Zip 功能時,常常我們所想到的都仿間常見的 Zip Library ,如:ICSarpCode.SharpZipLib、DotNetZip 等.. 其實 .NET Framework 的 System.IO.Compression 命名空間裡就有提供 Zip 的壓縮功能。
前言
長久以來,我們在 C# 裡需要使用 Zip 功能時,常常我們所想到的都仿間常見的 Zip Library ,如:ICSarpCode.SharpZipLib、DotNetZip 等.. 其實 .NET Framework 的 System.IO.Compression 命名空間裡就有提供 Zip 的壓縮功能
實作
一、建立 ZipArchive
在 System.IO.Compression 名命空間裡提供一個類別 ZipArchive 類別可以您建立一個 Zip 壓縮檔案的執行個體。我們先來看看建立 ZipArchive 物件時所需要參數
在初始化 ZipArchive 時,最多可以傳入三個參數
Stream:
同樣的,在 .NET Framework 裡面,對於檔案的處理都是透過 FileStream
ZipAcrchiveMode:
這個參數提供三種設定的方式,如下:
- Create :都用於建立新的 Zip 壓縮檔案的時候
- Read:用於讀取現有的 Zip 壓縮檔案 (這個時候不提供改變壓縮檔案內容的功能,唯讀處理)
- Update:用於更新現有 Zip 壓縮檔案時使用
leaveOpen:
是否在建立 ZipArchive 後維持資料流開啟狀態
程式碼如下:
var archive = new ZipArchive(fileStream, ZipArchiveMode.Create, true);
二、將要壓縮的實際檔案讀入 FileStream 中,並轉換為 byte[]
這邊的傳換要注意 Stream 類別中讀取為 byte 時的 offset 為 int ,我們知道 Int 為帶正負號的 32位元整數,當您壓縮的超大檔案可能長度限制問題,這邊筆者另外寫一個 BinaryReadToEnd() 方法來處理這件事情。
程式碼如下:
byte[] xlsxBytes = BinaryUtil.BinaryReadToEnd(f);
BinaryReadToEnd 的程式碼如下,我先讀入一個 buffer 中,所以在大的檔案都讀得進來,只要你的 RAM 夠大:
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;
}
}
}
三、在 ZipArchive 中建立操作的進入點
當我們取到 byte[] 後,接著就是在 Zip 檔案哩,建立它的名稱,你可以當作進入點,這個進入點你可以看就是當你使用 WinZip 或 7-Zip 的壓縮軟體開啟時,在壓縮軟體裡,滑鼠可以點選的那個檔案名稱。
var zipArchiveEntry = archive.CreateEntry(createEntryFileName, CompressionLevel.Fastest);
四、將 byte[] 寫入 zipArchiveEntry 中
這邊我們必須將 zipArchiveEntry 開啟,我們才可以將 byte[] 寫入該 zipArchiveEntry 中,程式碼如下:
using (var zipStream = zipArchiveEntry.Open())
{
zipStream.Write(xlsxBytes, 0, xlsxBytes.Length);
}
完成後,記得將 FileStream 做 Close 的處理。
完整的「壓縮」程式碼如下 (這邊的範例是寫成一個方法 Method):
public static string CreateZipArchiveByExcel(string sourceFile)
{
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);
BinaryReader bs = new BinaryReader(f);
using (var zipStream = zipArchiveEntry.Open())
{
zipStream.Write(xlsxBytes, 0, xlsxBytes.Length);
}
}
}
}
finally
{
f.Close();
}
return distinationFile;
}
結語:
有時筆者在替客戶撰寫 Console 的排程程式時,在部署時,因為客戶端環境的關係,一個 Console 的 EXE 檔案還得參照第三方的 Lib 時不免有點麻煩,如果 .NET Framework 即內建就盡量使用內建功能來達到需求。
提供各位參考。
簽名:
學習是一趟奇妙的旅程
這當中,有辛苦、有心酸、也有成果。有時也會有瓶頸。要能夠繼續勇往直前就必須保有一顆最熱誠的心。
軟體開發之路(FB 社團):https://www.facebook.com/groups/361804473860062/
Gelis 程式設計訓練營(粉絲團):https://www.facebook.com/gelis.dev.learning/
如果文章對您有用,幫我點一下讚,或是點一下『我要推薦』,這會讓我更有動力的為各位讀者撰寫下一篇文章。
非常謝謝各位的支持與愛護,小弟在此位各位說聲謝謝!!! ^_^