C#文件压缩与解压缩的多种实现方法
作者:MadeInSQL
内置类库方法
.NET Framework自4.5版本起提供了System.IO.Compression命名空间,其中包含实现文件压缩和解压缩的核心类:
ZipFile类 - 提供创建、提取和操作zip存档的静态方法
using System.IO.Compression; // 创建压缩文件 ZipFile.CreateFromDirectory(@"C:\data", @"C:\archive.zip"); // 解压文件 ZipFile.ExtractToDirectory(@"C:\archive.zip", @"C:\extracted");
GZipStream类 - 提供GZip数据格式的压缩和解压缩流
// 压缩文件 using (FileStream originalFileStream = File.OpenRead(@"C:\data.txt")) using (FileStream compressedFileStream = File.Create(@"C:\data.gz")) using (GZipStream compressionStream = new GZipStream(compressedFileStream, CompressionMode.Compress)) { originalFileStream.CopyTo(compressionStream); }
第三方库解决方案
当需要更高级的功能时,可以考虑以下第三方库:
SharpZipLib - 开源库,支持Zip、GZip、Tar和BZip2格式
using ICSharpCode.SharpZipLib.Zip; // 创建Zip文件 FastZip fastZip = new FastZip(); fastZip.CreateZip(@"C:\archive.zip", @"C:\data", true, "");
DotNetZip - 提供更简单的API和更好的性能
using Ionic.Zip; // 创建带密码保护的Zip using (ZipFile zip = new ZipFile()) { zip.Password = "secret"; zip.AddFile(@"C:\data.txt"); zip.Save(@"C:\secure.zip"); }
实际应用场景
日志文件压缩:定期压缩应用程序日志文件以节省存储空间
// 每周压缩日志文件 if (DateTime.Now.DayOfWeek == DayOfWeek.Monday) { var logFiles = Directory.GetFiles(@"C:\logs", "*.log"); using (var zip = new ZipFile()) { zip.AddFiles(logFiles); zip.Save($"logs_{DateTime.Now:yyyyMMdd}.zip"); } }文件上传前压缩:减少网络传输数据量
// 上传前压缩用户文件 public byte[] CompressFileForUpload(string filePath) { using (var ms = new MemoryStream()) using (var gz = new GZipStream(ms, CompressionMode.Compress)) using (var file = File.OpenRead(filePath)) { file.CopyTo(gz); return ms.ToArray(); } }批量处理:压缩多个文件后统一发送
public void BatchCompressAndSend(string[] files, string outputPath) { using (var zip = new ZipFile()) { foreach (var file in files) { zip.AddFile(file, ""); } zip.Save(outputPath); SendToServer(outputPath); } }
选择哪种方法取决于具体需求,内置类库适合基本需求,而第三方库则提供更多高级功能和更好的性能。
1. 使用System.IO.Compression命名空间
.NET Framework提供了System.IO.Compression命名空间来处理压缩和解压缩:
ZipFile类(需要引用System.IO.Compression.ZipFile)
using System.IO.Compression; // 压缩单个文件 ZipFile.CreateFromDirectory(@"C:\sourceFolder", @"C:\archive.zip"); // 解压缩文件 ZipFile.ExtractToDirectory(@"C:\archive.zip", @"C:\destinationFolder");
GZipStream和DeflateStream类
// 使用GZipStream压缩文件
using (FileStream originalFileStream = File.OpenRead(@"C:\largefile.dat"))
using (FileStream compressedFileStream = File.Create(@"C:\largefile.dat.gz"))
using (GZipStream compressionStream = new GZipStream(compressedFileStream, CompressionMode.Compress))
{
originalFileStream.CopyTo(compressionStream);
}
// 使用GZipStream解压文件
using (FileStream compressedFileStream = File.OpenRead(@"C:\largefile.dat.gz"))
using (FileStream outputFileStream = File.Create(@"C:\largefile.dat"))
using (GZipStream decompressionStream = new GZipStream(compressedFileStream, CompressionMode.Decompress))
{
decompressionStream.CopyTo(outputFileStream);
}
2. 使用第三方库(如SharpZipLib)
当需要更多功能时,可以使用第三方库如SharpZipLib:
using ICSharpCode.SharpZipLib.Zip; using ICSharpCode.SharpZipLib.Core; // 使用SharpZipLib创建ZIP文件 FastZip fastZip = new FastZip(); fastZip.CreateZip(@"C:\archive.zip", @"C:\sourceFolder", true, ""); // 使用SharpZipLib解压ZIP文件 fastZip.ExtractZip(@"C:\archive.zip", @"C:\destinationFolder", "");
3. 压缩和解压缩内存中的数据
// 压缩字节数组
public static byte[] Compress(byte[] data)
{
using (var compressedStream = new MemoryStream())
using (var zipStream = new GZipStream(compressedStream, CompressionMode.Compress))
{
zipStream.Write(data, 0, data.Length);
zipStream.Close();
return compressedStream.ToArray();
}
}
// 解压字节数组
public static byte[] Decompress(byte[] data)
{
using (var compressedStream = new MemoryStream(data))
using (var zipStream = new GZipStream(compressedStream, CompressionMode.Decompress))
using (var resultStream = new MemoryStream())
{
zipStream.CopyTo(resultStream);
return resultStream.ToArray();
}
}
4. 高级应用场景
密码保护的ZIP文件(使用SharpZipLib)
// 创建带密码的ZIP文件 ZipOutputStream zipStream = new ZipOutputStream(File.Create(@"C:\secure.zip")); zipStream.Password = "mypassword"; // 添加文件代码... zipStream.Close(); // 解压带密码的ZIP文件 ZipFile zipFile = new ZipFile(@"C:\secure.zip"); zipFile.Password = "mypassword"; // 解压代码...
分卷压缩(使用SharpZipLib)
ZipOutputStream zipStream = new ZipOutputStream(File.Create(@"C:\split.zip")); zipStream.SetLevel(5); // 压缩级别 zipStream.UseZip64 = UseZip64.On; // 支持大文件 zipStream.SetSplitSize(10000000); // 每卷10MB // 添加文件代码... zipStream.Close();
5. 性能优化建议
- 性能优化建议
- 缓冲流的使用 对于大文件操作,建议使用BufferedInputStream和BufferedOutputStream包装基础流。缓冲区大小可设置为8KB(8192字节)或更大的2的幂次方值。例如:
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("largefile.dat"), 8192);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("compressed.zip"), 8192);
- 压缩级别调优 ZipOutputStream支持设置压缩级别(0-9):
- 0(NO_COMPRESSION):仅存储,不压缩
- 1(BEST_SPEED):最快压缩速度
- 9(BEST_COMPRESSION):最高压缩率
- 默认值通常为6(平衡模式) 示例设置:
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream("output.zip"));
zos.setLevel(Deflater.BEST_COMPRESSION); // 设置为最高压缩率
- 多线程处理 对于批量文件处理场景:
- 使用线程池(如ExecutorService)
- 按文件数量或大小划分任务
- 注意线程安全,避免共享ZipOutputStream实例 典型应用场景:
- 服务器日志批量压缩归档
- 分布式系统中的分片压缩
- 资源重用优化 对于高频操作:
- 将ZipFile实例缓存为类成员变量
- 使用对象池模式管理ZipFile实例
- 注意及时关闭长期不用的实例 重用示例:
public class ZipManager {
private static Map<String, ZipFile> zipFileCache = new ConcurrentHashMap<>();
public static ZipFile getZipFile(String path) throws IOException {
return zipFileCache.computeIfAbsent(path, p -> new ZipFile(p));
}
}
附加建议:
- 对于超大型文件(>1GB),考虑使用Zip64格式
- 监控内存使用情况,避免OOM
- 在SSD存储设备上操作可获得更好的I/O性能
6. 错误处理
在实际应用中,应该添加适当的错误处理:
try
{
ZipFile.ExtractToDirectory(sourceZipPath, extractPath);
}
catch (IOException ex) when (ex.Message.Contains("already exists"))
{
// 处理文件已存在的情况
}
catch (UnauthorizedAccessException)
{
// 处理权限问题
}
catch (Exception ex)
{
// 处理其他异常
}
C#解压文件跨平台问题详解
跨平台解压的主要挑战
在C#中进行跨平台解压时,主要面临以下几个技术挑战:
路径分隔符差异
Windows使用反斜杠()作为路径分隔符,而Linux/macOS使用正斜杠(/)。当解压包含路径的文件时,可能导致路径解析错误。文件名大小写敏感性问题
Linux/macOS文件系统区分文件名大小写,而Windows不区分。解压后可能导致文件访问问题。文件权限保留问题
Unix系统中文件权限信息(如可执行权限)在Windows上可能丢失或被忽略。编码问题
不同平台对文件名编码的处理可能存在差异,特别是非ASCII字符的文件名。
常用解决方案
1. 使用System.IO.Compression
using System.IO.Compression;
// 解压ZIP文件
ZipFile.ExtractToDirectory("archive.zip", "output_folder");
跨平台注意事项:
- 在.NET Core 2.0+和.NET 5+中完全支持跨平台
- 自动处理路径分隔符转换
- 不保留Unix文件权限
2. 使用SharpZipLib
using ICSharpCode.SharpZipLib.Zip;
// 解压ZIP文件
var fastZip = new FastZip();
fastZip.ExtractZip("archive.zip", "output_folder", null);
优势:
- 支持更多压缩格式(zip, gzip, tar等)
- 可配置路径转换行为
- 更细粒度的控制选项
3. 使用DotNetZip
using Ionic.Zip;
// 解压ZIP文件
using (var zip = ZipFile.Read("archive.zip"))
{
zip.ExtractAll("output_folder", ExtractExistingFileAction.OverwriteSilently);
}
特点:
- 支持密码保护压缩文件
- 可处理大文件
- 提供进度报告功能
最佳实践建议
- 路径处理:
- 总是使用
Path.Combine()构建路径 - 使用
Path.DirectorySeparatorChar代替硬编码分隔符
- 总是使用
string fullPath = Path.Combine("folder", "subfolder", "file.txt");
文件名规范化:
- 对文件名进行规范化处理,避免大小写问题
- 考虑使用
StringComparer.OrdinalIgnoreCase进行文件名比较
编码指定:
- 显式指定编码(如UTF-8)处理非ASCII文件名
// 使用SharpZipLib时指定编码
var zip = new ZipFile("archive.zip") {
NameTransform = new ZipNameTransform("output_folder"),
StringCodec = StringCodec.FromCodePage(Encoding.UTF8.CodePage)
};
- 权限处理:
- 对于需要保留Unix权限的场景,考虑使用
Mono.Posix库 - 或在解压后手动设置文件权限
- 对于需要保留Unix权限的场景,考虑使用
实际应用场景示例
场景:跨平台Web应用中解压上传的文件
public async Task<string> ExtractUploadedZip(IFormFile zipFile, string extractPath)
{
// 确保目标目录存在
Directory.CreateDirectory(extractPath);
// 保存上传的ZIP文件
var tempZipPath = Path.GetTempFileName();
using (var stream = new FileStream(tempZipPath, FileMode.Create))
{
await zipFile.CopyToAsync(stream);
}
try
{
// 跨平台解压
ZipFile.ExtractToDirectory(tempZipPath, extractPath);
// 处理解压后的文件(如设置权限)
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
var files = Directory.GetFiles(extractPath, "*", SearchOption.AllDirectories);
foreach (var file in files)
{
// 设置Unix权限(如644)
File.SetUnixFileMode(file, UnixFileMode.UserRead | UnixFileMode.UserWrite
| UnixFileMode.GroupRead | UnixFileMode.OtherRead);
}
}
return extractPath;
}
finally
{
File.Delete(tempZipPath);
}
}
常见问题及解决方案
"路径中包含非法字符"错误
问题描述:当尝试在Windows、Linux或macOS上创建或访问文件路径时,可能会遇到因路径包含系统保留字符而导致的错误。
详细解决方案:
- 使用
System.IO.Path.GetInvalidPathChars()方法获取当前操作系统不允许的字符列表 - 实现路径清理函数,例如:
public static string SanitizePath(string path) { var invalidChars = Path.GetInvalidPathChars(); return string.Concat(path.Where(c => !invalidChars.Contains(c))); } - 对于特殊场景(如网络路径),可能需要额外处理正斜杠/反斜杠转换
解压后文件权限丢失问题
问题背景:在Linux/macOS系统中,文件权限位(755、644等)对于程序执行至关重要,但Windows创建的ZIP文件通常不保存这些信息。
解决方案选项:
- .NET 6+原生方案:
File.SetUnixFileMode(filePath, UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute);
- 跨平台方案:
- 调用系统命令:
Process.Start("chmod", $"755 {filePath}"); - 使用第三方库如Mono.Posix.NETStandard
- 调用系统命令:
文件名乱码问题
典型场景:当解压由不同语言系统创建的压缩包时(如日文Windows创建的ZIP在中文Linux上解压),文件名可能出现乱码。
解决方案:
- 明确指定编码格式:
using (var archive = ZipFile.OpenRead(zipPath)) { var options = new ZipArchiveEncoding { Default = Encoding.GetEncoding(932) // 日语Shift-JIS编码 }; // 使用指定编码处理条目 } - 推荐做法:
- 创建压缩包时使用UTF-8编码
- 使用支持Zip64扩展的库(如SharpZipLib、DotNetZip)
大文件解压内存不足
优化策略:
- 采用流式处理替代全内存加载:
using (var zipStream = new ZipInputStream(File.OpenRead(zipPath))) { ZipEntry entry; while ((entry = zipStream.GetNextEntry()) != null) { using (var fileStream = File.Create(entry.Name)) { zipStream.CopyTo(fileStream); } } } - 内存管理建议:
- 设置缓冲区大小(通常8KB-64KB为佳)
- 对大文件实现分块处理
- 考虑使用
MemoryMappedFile处理超大文件
跨平台兼容性最佳实践
路径处理:
- 始终使用
Path.Combine()构建路径 - 避免硬编码目录分隔符(使用
Path.DirectorySeparatorChar)
- 始终使用
文件系统观察:
- 使用
FileSystemWatcher时要考虑不同系统的通知机制差异
- 使用
测试矩阵建议:
- Windows (NTFS)
- Linux (ext4)
- macOS (APFS)
- 包括不同语言环境测试
通过实施这些解决方案,可以显著提高C#解压功能在混合环境中的可靠性和兼容性。
到此这篇关于C#文件压缩与解压缩的多种实现方法的文章就介绍到这了,更多相关C#文件压缩与解压缩内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
