在C#项目中处理文件归档时,System.IO.Compression命名空间下的ZipArchive类是最直接的选择。它从.NET Framework 4.5开始内置,不需要引入第三方动态库,就能以流的方式创建或读取zip包。与早期依赖外部命令行工具的方式相比,ZipArchive完全运行在托管代码内,可以更精细地控制每一个条目的压缩级别、文件时间和内容写入过程。

使用ZipArchive创建压缩包并写入文件夹
创建zip包的核心思路是先打开一个可写流,再用ZipArchive的Create模式实例化,随后通过CreateEntry方法生成条目。如果要把整个文件夹打包,必须自己递归遍历目录树,因为ZipArchive不会自动把子目录结构展开。每遇到一个文件,就创建对应相对路径的条目,并把文件流复制进条目的Open流里。
需要注意,zip内部条目路径使用正斜杠分隔,而Windows本地路径是反斜杠。在拼接条目名时应当把反斜杠替换成斜杠,否则部分解压软件会识别异常。另外空文件夹在zip里表现为末尾带斜杠的条目,如果业务要求保留目录结构,应当显式创建这种条目。
using System;
using System.IO;
using System.IO.Compression;
public static void ZipFolder(string sourceDir, string zipPath)
{
if (File.Exists(zipPath))
File.Delete(zipPath);
using (FileStream fs = new FileStream(zipPath, FileMode.Create))
using (ZipArchive archive = new ZipArchive(fs, ZipArchiveMode.Create))
{
// 递归写入文件和空目录
foreach (string file in Directory.GetFiles(sourceDir, "*", SearchOption.AllDirectories))
{
string relative = file.Substring(sourceDir.Length).TrimStart('\\');
string entryName = relative.Replace('\\', '/');
archive.CreateEntryFromFile(file, entryName);
}
foreach (string dir in Directory.GetDirectories(sourceDir, "*", SearchOption.AllDirectories))
{
string relative = dir.Substring(sourceDir.Length).TrimStart('\\');
string entryName = relative.Replace('\\', '/') + "/";
archive.CreateEntry(entryName);
}
}
}
上面的代码利用了CreateEntryFromFile简化文件复制,同时手动补建空目录条目。相对路径计算时用了TrimStart('\\')去掉盘符后的反斜杠,确保条目不以斜杠开头。这种方式在中小型目录打包时性能足够,若文件极多可考虑异步流复制降低内存占用。
从ZipArchive读取并解压到本地磁盘
解压过程与压缩相反,以Read模式打开ZipArchive后遍历Entries集合。每个ZipArchiveEntry都有FullName属性表示包内路径,用这个路径在目标文件夹下重建本地文件即可。由于FullName可能包含多层目录,写入前必须确保父目录存在,否则会抛出目录不存在异常。
在提取条目内容时,应当用entry.Open()获取只读流,再复制到目标文件流。对于那种末尾带斜杠的空目录条目,FullName会以斜杠结束,此时只需创建目录而不写文件。处理完所有条目后,本地结构与压缩前基本一致。
using System;
using System.IO;
using System.IO.Compression;
public static void UnzipToFolder(string zipPath, string destDir)
{
using (FileStream fs = new FileStream(zipPath, FileMode.Open))
using (ZipArchive archive = new ZipArchive(fs, ZipArchiveMode.Read))
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
string destPath = Path.Combine(destDir, entry.FullName);
if (entry.FullName.EndsWith("/"))
{
Directory.CreateDirectory(destPath);
continue;
}
Directory.CreateDirectory(Path.GetDirectoryName(destPath));
using (Stream src = entry.Open())
using (FileStream outFs = new FileStream(destPath, FileMode.Create))
{
src.CopyTo(outFs);
}
}
}
}
这段代码对空目录做了跳过写文件的处理,并用Path.Combine拼接本地路径。由于Path.Combine在Windows下会自动使用反斜杠,不需要我们手动干预。若解压不可信来源的zip,还应检查FullName是否包含..防止路径穿越,把文件写到目标文件夹之外。
中文文件名乱码与压缩级别控制
默认情况下ZipArchive使用UTF-8编码条目标题,但旧版Windows资源管理器有时按系统本地编码读取,导致中文名乱码。可以在创建条目后显式设置entry.Name或借助注释里的编码标记,不过更稳妥的做法是确保生成环境使用较新的.NET且解压端支持UTF-8。若必须兼容老系统,可考虑第三方库指定传统编码,但原生类本身不直接暴露编码属性。
压缩级别通过CompressionLevel枚举控制,有Optimal、Fastest和NoCompression三种。Optimal体积最小但耗CPU,Fastest速度优先,NoCompression仅存储不压缩,适合已经压缩过的媒体文件。在创建条目时通过CreateEntry重载传入级别即可平衡效率。
using System.IO.Compression;
// 用较快速度压缩单个文件
using (FileStream fs = new FileStream("out.zip", FileMode.Create))
using (ZipArchive archive = new ZipArchive(fs, ZipArchiveMode.Create))
{
ZipArchiveEntry entry = archive.CreateEntry("数据/报表.txt", CompressionLevel.Fastest);
using (Stream writer = entry.Open())
using (FileStream src = new FileStream("C:\\Data\\报表.txt", FileMode.Open))
{
src.CopyTo(writer);
}
}
上述示例把本地C:\Data\报表.txt以Fastest级别写入zip内数据目录。可以看到条目名直接写了正斜杠分隔的层级,这样解压后会自动建立数据文件夹。掌握这些细节后,C#处理zip打包解包就能摆脱外部依赖,在服务器或无界面的服务进程里稳定运行。
C# ZipArchive文件夹打包文件压缩解压修改时间:2026-08-23 20:18:57