在C#项目中把文件或文件夹打包成zip,最标准且零依赖的做法是使用基础类库中的System.IO.Compression命名空间。该命名空间从.NET Framework 4.5起就内置于框架,不需要安装任何第三方组件,也不依赖外部压缩程序。理解这套API的两种主要使用方式,能应对从简单备份到精细打包的不同需求。

一、使用ZipFile快速压缩整个文件夹
如果需求只是把某个目录完整地打成zip包,ZipFile类的静态方法是最省事的方案。它一行调用就能递归处理子目录,并自动维护文件夹结构。这种方式适合定时备份、导出用户数据等不需要干预内部条目的场景。
下面的示例演示如何将D:data文件夹压缩为D:data.zip,并采用最快压缩级别以减少CPU占用:
using System;
using System.IO;
using System.IO.Compression;
class Program
{
static void Main()
{
string sourceDir = @"D:data";
string zipPath = @"D:data.zip";
// 若已存在则先删除,避免抛出异常
if (File.Exists(zipPath))
{
File.Delete(zipPath);
}
// 第二个参数控制是否包含基目录名,第三个参数设压缩级别
ZipFile.CreateFromDirectory(
sourceDir,
zipPath,
CompressionLevel.Fastest,
includeBaseDirectory: false
);
Console.WriteLine("文件夹已压缩为zip");
}
}
代码中includeBaseDirectory参数很关键。若设为true,zip内会多一层以源文件夹名为名的根目录;设为false则直接把内容放在zip根下。压缩级别CompressionLevel.Optimal体积更小但更慢,Fastest反之。实际选型应结合文件大小和机器负载。
这种方案的局限在于无法过滤文件,也不能在打包时改名或加密。当业务逻辑要求只打包某种后缀或排除临时文件时,就需要更底层的ZipArchive。
二、使用ZipArchive精细控制每个条目
ZipArchive以流的方式工作,开发者可以自行遍历文件、创建条目并写入内容。它适合需要条件打包、动态命名或边生成边压缩的场合,比如把数据库查询结果和日志一起归档。
以下代码将指定文件夹下的所有txt文件打包,且在zip内统一放到logs子目录中:
using System;
using System.IO;
using System.IO.Compression;
class Program
{
static void Main()
{
string sourceDir = @"D:data";
string zipPath = @"D:texts.zip";
using (FileStream fs = new FileStream(zipPath, FileMode.Create))
using (ZipArchive archive = new ZipArchive(fs, ZipArchiveMode.Create))
{
foreach (string filePath in Directory.GetFiles(sourceDir, "*.txt", SearchOption.AllDirectories))
{
// 计算相对路径,并加上logs前缀
string relative = Path.GetRelativePath(sourceDir, filePath);
string entryName = "logs/" + relative.Replace("\", "/");
ZipArchiveEntry entry = archive.CreateEntry(entryName, CompressionLevel.Optimal);
using (Stream entryStream = entry.Open())
using (FileStream src = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
src.CopyTo(entryStream);
}
}
}
Console.WriteLine("筛选后的txt已打包");
}
}
这里用Path.GetRelativePath得到相对于源目录的路径,再手动拼接目标结构。注意路径分隔符在zip内部统一用斜杠。通过CreateEntry的重载可以逐条设定压缩级别,对大文件和小文件区别对待能进一步提升效率。
相比ZipFile,ZipArchive写起来繁琐,但灵活性高。若要在条目写入前做内容转换,比如把文本转成utf-8再存,只需在CopyTo前加一层StreamWriter即可,这是静态方法做不到的。
三、常见误区与注意事项
不少开发者误以为C#做zip必须引用第三方库,于是项目里混进各种压缩组件,增加体积与漏洞风险。实际上基础类库已足够成熟。另一个误区是忽略文件路径中的非法字符,当源文件含冒号等字符时,CreateEntry会报错,需提前清洗条目名。
还有人在循环里反复打开同一个ZipArchive写条目,导致流状态异常。正确做法是在using块内一次性建好归档,所有条目都在该块中完成。下表列出两种方案的核心差异:
| 对比项 | ZipFile | ZipArchive |
|---|---|---|
| 使用复杂度 | 低,一行调用 | 高,需手动遍历 |
| 过滤文件 | 不支持 | 支持 |
| 修改条目名 | 不支持 | 支持 |
| 适用场景 | 整目录备份 | 条件打包、流式处理 |
在权限方面,运行程序的账号需对源目录有读取权、对目标路径有写入权。若涉及网络盘,还要注意连接中断导致的半包问题,建议在finally中校验zip完整性。
四、解压与验证的简单补充
打包之后通常还要能解压。基础类库同样提供ZipFile.ExtractToDirectory方法,可把zip还原到文件夹。若只想知道包内有哪些条目,用ZipArchive读取Entries属性即可,无需真正解压。
示例展示如何安全解压并列出内容:
using System;
using System.IO;
using System.IO.Compression;
class Program
{
static void Main()
{
string zipPath = @"D:data.zip";
string destDir = @"D:out";
using (ZipArchive archive = ZipFile.OpenRead(zipPath))
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
Console.WriteLine(entry.FullName);
}
}
ZipFile.ExtractToDirectory(zipPath, destDir, overwriteFiles: true);
Console.WriteLine("解压完成");
}
}
以上代码先只读打开归档打印条目,再覆盖式解压到目标目录。overwriteFiles参数在.NET Core及后续版本可用,能避免文件已存在异常。掌握打包与解压两端,就能在C#中完整处理zip生命周期。
C#zip压缩System_IO_Compression修改时间:2026-07-31 15:33:16