在C#项目开发中,经常需要将包含多级子目录的文件夹完整打包成压缩文件,同时要求解压后目录结构和原文件夹完全一致,保留所有文件的相对路径。SharpZipLib是一款功能强大的.NET开源压缩库,支持Zip、GZip、Tar等多种压缩格式,能够轻松实现这类需求。

环境准备
首先需要在项目中引入SharpZipLib库,可以通过NuGet包管理器搜索SharpZipLib进行安装,也可以手动下载对应的DLL文件添加到项目引用中。安装完成后,在代码文件顶部添加对应的命名空间引用:
using ICSharpCode.SharpZipLib.Zip; using System.IO;
核心实现逻辑
实现多级目录压缩并保留相对路径的核心思路是:遍历目标文件夹下的所有文件和子目录,将每个文件的相对路径作为ZipEntry的名称写入压缩流,这样解压时就会自动还原原有的目录结构。
完整代码实现
以下是完整的压缩方法实现,支持指定源目录、输出压缩文件路径,以及是否包含根目录本身:
/// <summary>
/// 使用SharpZipLib压缩多级目录并保留相对路径
/// </summary>
/// <param name="sourceDir">待压缩的源目录路径</param>
/// <param name="zipFilePath">生成的压缩文件路径</param>
/// <param name="includeRootDir">是否将根目录本身包含在压缩包内</param>
public static void CompressDirectory(string sourceDir, string zipFilePath, bool includeRootDir = false)
{
// 确保源目录存在
if (!Directory.Exists(sourceDir))
{
throw new DirectoryNotFoundException($"源目录不存在: {sourceDir}");
}
// 创建文件流用于写入压缩文件
using (FileStream fs = new FileStream(zipFilePath, FileMode.Create, FileAccess.Write))
using (ZipOutputStream zipStream = new ZipOutputStream(fs))
{
// 设置压缩级别,0-9,9为最高压缩率
zipStream.SetLevel(6);
// 获取源目录的父目录,用于计算相对路径
string parentDir = Path.GetDirectoryName(sourceDir);
// 处理根目录名称,用于相对路径拼接
string rootDirName = Path.GetFileName(sourceDir);
// 递归压缩目录下的所有文件和子目录
CompressDirectoryRecursive(sourceDir, parentDir, rootDirName, includeRootDir, zipStream);
zipStream.Finish();
zipStream.Close();
}
}
/// <summary>
/// 递归压缩目录内容
/// </summary>
private static void CompressDirectoryRecursive(string currentDir, string parentDir, string rootDirName, bool includeRootDir, ZipOutputStream zipStream)
{
// 如果是根目录且需要包含根目录本身,先创建根目录的ZipEntry
if (includeRootDir && currentDir == Path.Combine(parentDir, rootDirName))
{
string rootEntryName = rootDirName + "/";
ZipEntry rootEntry = new ZipEntry(rootEntryName);
rootEntry.DateTime = DateTime.Now;
zipStream.PutNextEntry(rootEntry);
zipStream.CloseEntry();
}
// 处理当前目录下的所有文件
string[] files = Directory.GetFiles(currentDir);
foreach (string file in files)
{
// 计算文件的相对路径
string relativePath;
if (includeRootDir)
{
// 相对路径从根目录开始
relativePath = Path.Combine(rootDirName, file.Substring(parentDir.Length + 1 + rootDirName.Length).TrimStart(Path.DirectorySeparatorChar));
}
else
{
// 相对路径从根目录下的内容开始
relativePath = file.Substring(parentDir.Length + 1 + rootDirName.Length).TrimStart(Path.DirectorySeparatorChar);
}
// 替换路径分隔符为Zip标准的正斜杠
relativePath = relativePath.Replace(Path.DirectorySeparatorChar, '/');
// 创建文件对应的ZipEntry
ZipEntry entry = new ZipEntry(relativePath);
entry.DateTime = File.GetLastWriteTime(file);
zipStream.PutNextEntry(entry);
// 读取文件内容写入压缩流
using (FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read))
{
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) > 0)
{
zipStream.Write(buffer, 0, bytesRead);
}
}
zipStream.CloseEntry();
}
// 递归处理子目录
string[] subDirs = Directory.GetDirectories(currentDir);
foreach (string subDir in subDirs)
{
CompressDirectoryRecursive(subDir, parentDir, rootDirName, includeRootDir, zipStream);
}
}
代码使用说明
调用上述压缩方法非常简单,只需要传入对应的参数即可:
// 示例:压缩D盘下的TestDir目录,生成压缩文件到D盘根目录,包含根目录本身 string sourceDirectory = @"D:TestDir"; string outputZipPath = @"D:TestDir.zip"; CompressDirectory(sourceDirectory, outputZipPath, true);
关键注意事项
- 路径分隔符处理:Zip格式标准要求路径使用正斜杠
/,因此需要将Windows系统的反斜杠替换为/,否则可能出现解压后路径异常的问题。 - 压缩级别设置:
SetLevel方法支持0到9的压缩级别,级别越高压缩率越高,但压缩耗时也会相应增加,可根据实际需求调整。 - 相对路径计算:核心是通过源目录的父目录和根目录名称,计算出每个文件相对于源目录的路径,确保写入ZipEntry的名称正确。
- 资源释放:使用
using语句确保文件流和压缩流被正确释放,避免出现文件占用的问题。
常见问题处理
如果压缩后的文件解压后目录层级丢失,通常是因为ZipEntry的名称没有正确设置相对路径,需要检查相对路径的计算逻辑是否正确。如果出现文件写入失败的问题,需要检查目标压缩文件的目录是否有写入权限,以及源文件是否被其他进程占用。
C#SharpZipLib多级目录压缩相对路径保留修改时间:2026-07-14 07:12:27