鑒於許多博客講C#打包Zip的都寫的相對啰嗦且沒有注釋,不適合小白觀看,特寫此篇講述一下
先貼代碼,直接創建一個.Net Core控制台就能運行

using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; namespace Zip { class Program { static void Main(string[] args) { var zipFiles = new List<ZipFile> { new ZipFile { RelativePath = "1/2/3/公文簽批單223.pdf", Content = Extend.GetBytesFromPath("E:/公文簽批單.pdf") }, new ZipFile { RelativePath = "1/2/3/公文簽批單2233.pdf", //相同路徑下文件名重名時,解壓時會提示是否覆蓋 Content = Extend.GetBytesFromPath("E:/公文簽批單.pdf") } }; var zipBytes = zipFiles.PackedZip(); zipBytes.WriteToFile("E:/222/Zip測試.zip"); Console.WriteLine("打包Zip成功"); Console.ReadKey(); } } public static class Extend { /// <summary> /// 將流轉成字節數組 /// </summary> /// <param name="stream">文件流</param> /// <returns></returns> public static byte[] ToBytes(this Stream stream) { byte[] bytes = new byte[stream.Length]; stream.Read(bytes, 0, bytes.Length); // 設置當前流的位置為流的開始 stream.Seek(0, SeekOrigin.Begin); return bytes; } /// <summary> /// 獲取文件的內容(字節數組) /// </summary> /// <param name="absolutePath">絕對路徑</param> /// <returns></returns> public static byte[] GetBytesFromPath(string absolutePath) { using (Stream fileStream = new FileStream(absolutePath, FileMode.OpenOrCreate)) { return fileStream.ToBytes(); } } /// <summary> /// 字節數組寫入文件 /// </summary> /// <param name="bytes">文件內容</param> /// <param name="absolutePath">絕對路徑</param> public static void WriteToFile(this byte[] bytes, string absolutePath) { if (File.Exists(absolutePath)) { File.Delete(absolutePath); } using (var fs = new FileStream(absolutePath, FileMode.Create)) { fs.Write(bytes, 0, bytes.Length); } } /// <summary> /// 將Zip文件描述對象打包成Zip文件,並返回字節數組 /// </summary> /// <param name="zipFiles">Zip文件描述對象數組</param> /// <returns></returns> public static byte[] PackedZip(this List<ZipFile> zipFiles) { using (Stream memoryStream = new MemoryStream()) { using (var zipArchive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true)) { foreach (var zipFile in zipFiles) { zipArchive.AddFile(zipFile.RelativePath, zipFile.Content); } } memoryStream.Seek(0, SeekOrigin.Begin); //這句要在using ZipArchive外面,否則壓縮文件會被損壞 return memoryStream.ToBytes(); } } /// <summary> /// 向Zip文件中添加文件 /// </summary> /// <param name="zipArchive"></param> /// <param name="relativePath">相對路徑</param> /// <param name="bytes">文件內容</param> /// <returns></returns> private static bool AddFile(this ZipArchive zipArchive, string relativePath, byte[] bytes) { try { ZipArchiveEntry entry = zipArchive.CreateEntry(relativePath); using (Stream entryStream = entry.Open()) { entryStream.Write(bytes, 0, bytes.Length); } return true; } catch { return false; } } } /// <summary> /// 描述打包成Zip時的一個文件的信息 /// </summary> public class ZipFile { /// <summary> /// 文件相對路徑,帶文件名和拓展名 /// </summary> public string RelativePath { get; set; } /// <summary> /// 字節數組(文件內容) /// </summary> public byte[] Content { get; set; } } }
打包時用的是.Net自帶ZipArchive類,此類在System.IO.Compression.dll的程序集的System.IO.Compression命名空間下,一般裝了VS都有,想用其他別的類庫操作Zip的可以上Nuget找,但筆者寫此文時用的最多就是這個
基本流程如下:
1、獲得文件流(示例代碼是用FileStream讀取物理文件),並把文件流轉成字節數組;(在操作文件流時,常用的橋接方式就是用字節數組,至少我們公司封裝的方法就是如此)【如上面代碼的GetBytesFromPath方法】
2、創建一個MemeryStream(內存流)對象,並用這個MemeryStream對象初始化ZipArchive對象(ZipArchive是.Net自帶的一個操作Zip的類)【如上面代碼的PackedZip方法】
p.s. 許多博客都用的物理文件初始化,那結果只能保存回那個物理文件,ZipArchive類本身不具備導出流對象的方法;但用流初始化呢,當對ZipArchive進行修改時都會同步到流對象,有了流對象就有了字節數組,我既可以保存回物理文件,也可以把流傳到網絡上任何地方
3、用ZipArchive對象創建ZipArchiveEntry對象,並打開ZipArchiveEntry對象的流,往流里寫入文件,這樣就相當於往這個Zip文件里面裝文件了,創建ZipArchiveEntry對象時使用的是相對路徑,文件夾不需要實際存在【如上面代碼的AddFile方法】
4、當裝填好Zip對象之后就把之前初始化ZipArchive對象的流轉成字節數組返回,至此就大功告成了,之后要導出到物理文件或網絡上都隨你
附錄,微軟官方的教程: