.NET 4.5 起加入 ZipArchive、ZipFile 等列類別,自此不用額外安裝第三方程式庫就能製作 ZIP 檔。微軟官方文件則有篇範例文章,操作說明:壓縮與解壓縮檔案 - Microsoft Docs,介紹如何使用 System.IO.Compression 的一系列類別壓縮及解壓縮檔案。
我遇到一個需求,要將使用者在網站查詢的結果,以一筆資料一個檔案形式匯出,再集結壓縮成單一 ZIP 檔方便使用者下載。爬文找到的範例多以檔案形式處理為主,而我想省掉將資料寫成檔案再壓縮的步驟,但直接將記憶體 byte[] 壓成 ZIP(也是 byte[])的完整範例不多,索性將摸索成果整理成筆記:如何將記憶體中的 byte[] 直接壓成 ZIP 保存於記憶體?如此,直接 Response.BinaryWrite() 即可下載,全程資料不落地(不寫暫存檔),有利減少IO、提升效能。(註:前題是處理資料量不構成伺服器記憶體的壓力)
完整程式範例如下:
class Program
{
static void Main(string[] args)
{
var src = new Dictionary<string, byte[]>()
{
["name.txt"] = Encoding.UTF8.GetBytes("Jeffrey"),
["score.txt"] = Encoding.UTF8.GetBytes("32767")
};
var zip = ZipHelper.ZipData(src);
System.IO.File.WriteAllBytes("test.zip", zip);
var res = ZipHelper.UnzipData(zip);
foreach (var fileName in res.Keys)
{
Console.WriteLine($"FileName={fileName}");
Console.WriteLine($"Content={Encoding.UTF8.GetString(res[fileName])}");
}
Console.Read();
}
}
public class ZipHelper
{
public static byte[] ZipData(Dictionary<string, byte[]> data)
{
using (var zipStream = new MemoryStream())
{
using (var zipArchive = new ZipArchive(zipStream, ZipArchiveMode.Update))
{
foreach (var fileName in data.Keys)
{
var entry = zipArchive.CreateEntry(fileName);
using (var entryStream = entry.Open())
{
var buff = data[fileName];
entryStream.Write(buff, 0, buff.Length);
}
}
}
return zipStream.ToArray();
}
}
public static Dictionary<string, byte[]> UnzipData(byte[] zip)
{
var dict = new Dictionary<string, byte[]>();
using (var msZip = new MemoryStream(zip))
{
using (var archive = new ZipArchive(msZip, ZipArchiveMode.Read))
{
archive.Entries.ToList().ForEach(entry =>
{
//e.FullName可取得完整路徑
if (string.IsNullOrEmpty(entry.Name)) return;
using (var entryStream = entry.Open())
{
using (var msEntry = new MemoryStream())
{
entryStream.CopyTo(msEntry);
dict.Add(entry.Name, msEntry.ToArray());
}
}
});
}
}
return dict;
}
}
實測將 Dictionary<string, byte[]> 壓成 ZIP,解壓後內容還原無誤:
將壓縮結果另存 ZIP 檔,使用 7-Zip 可正常檢視解壓,測試成功!