《C#压缩解压帮助类》
在.NET开发中,文件压缩与解压是常见的需求场景,例如数据传输优化、存储空间节省或日志归档等。C#通过`System.IO.Compression`命名空间提供了基础的压缩解压功能,但实际应用中可能需要更灵活的封装。本文将详细介绍如何构建一个完整的C#压缩解压帮助类,涵盖ZIP、GZIP两种主流格式,并支持密码保护、分卷压缩等高级功能。
一、基础压缩解压实现
1.1 ZIP格式压缩
使用`ZipFile`类(需引入`System.IO.Compression.FileSystem`)可快速实现目录或文件的压缩:
using System.IO.Compression;
public static void CompressToZip(string sourcePath, string zipPath)
{
if (Directory.Exists(sourcePath))
{
ZipFile.CreateFromDirectory(sourcePath, zipPath);
}
else if (File.Exists(sourcePath))
{
string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
Directory.CreateDirectory(tempDir);
File.Move(sourcePath, Path.Combine(tempDir, Path.GetFileName(sourcePath)));
ZipFile.CreateFromDirectory(tempDir, zipPath);
Directory.Delete(tempDir, true);
}
}
1.2 ZIP格式解压
解压操作需处理路径不存在的情况:
public static void ExtractFromZip(string zipPath, string destPath)
{
if (!Directory.Exists(destPath))
{
Directory.CreateDirectory(destPath);
}
ZipFile.ExtractToDirectory(zipPath, destPath);
}
1.3 GZIP格式压缩解压
GZIP适用于单文件压缩,通过`GZipStream`实现:
public static void CompressWithGzip(string sourceFile, string destFile)
{
using (FileStream sourceStream = File.OpenRead(sourceFile))
using (FileStream destStream = File.Create(destFile))
using (GZipStream gzipStream = new GZipStream(destStream, CompressionLevel.Optimal))
{
sourceStream.CopyTo(gzipStream);
}
}
public static void DecompressWithGzip(string sourceFile, string destFile)
{
using (FileStream sourceStream = File.OpenRead(sourceFile))
using (FileStream destStream = File.Create(destFile))
using (GZipStream gzipStream = new GZipStream(sourceStream, CompressionMode.Decompress))
{
gzipStream.CopyTo(destStream);
}
}
二、高级功能实现
2.1 密码保护压缩
原生.NET不支持ZIP密码,需借助第三方库如SharpZipLib:
// 安装NuGet包:SharpZipLib
using ICSharpCode.SharpZipLib.Zip;
public static void CompressWithPassword(string sourcePath, string zipPath, string password)
{
using (FileStream fsOut = File.Create(zipPath))
using (ZipOutputStream zipStream = new ZipOutputStream(fsOut))
{
zipStream.Password = password;
if (Directory.Exists(sourcePath))
{
foreach (string file in Directory.GetFiles(sourcePath))
{
AddFileToZip(zipStream, file, "");
}
}
else if (File.Exists(sourcePath))
{
AddFileToZip(zipStream, sourcePath, "");
}
}
}
private static void AddFileToZip(ZipOutputStream zipStream, string filePath, string entryName)
{
string entry = string.IsNullOrEmpty(entryName)
? Path.GetFileName(filePath)
: Path.Combine(entryName, Path.GetFileName(filePath));
ZipEntry zipEntry = new ZipEntry(entry);
zipStream.PutNextEntry(zipEntry);
using (FileStream fsReader = File.OpenRead(filePath))
{
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = fsReader.Read(buffer, 0, buffer.Length)) > 0)
{
zipStream.Write(buffer, 0, bytesRead);
}
}
zipStream.CloseEntry();
}
2.2 分卷压缩实现
通过计算文件大小实现分卷:
public static void SplitCompress(string sourcePath, string baseName, long maxSizeBytes)
{
if (Directory.Exists(sourcePath))
{
string tempZip = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString() + ".zip");
ZipFile.CreateFromDirectory(sourcePath, tempZip);
SplitFile(tempZip, baseName, maxSizeBytes);
File.Delete(tempZip);
}
else if (File.Exists(sourcePath))
{
SplitFile(sourcePath, baseName, maxSizeBytes);
}
}
private static void SplitFile(string sourceFile, string baseName, long maxSizeBytes)
{
byte[] buffer = new byte[4096];
long bytesRemaining = new FileInfo(sourceFile).Length;
int partNumber = 0;
using (FileStream sourceStream = File.OpenRead(sourceFile))
{
while (bytesRemaining > 0)
{
string partName = $"{baseName}.part{++partNumber}.zip";
long bytesToWrite = Math.Min(maxSizeBytes, bytesRemaining);
using (FileStream destStream = File.Create(partName))
{
int bytesRead;
long totalBytesWritten = 0;
while (totalBytesWritten 0)
{
destStream.Write(buffer, 0, bytesRead);
totalBytesWritten += bytesRead;
}
}
bytesRemaining -= bytesToWrite;
}
}
}
三、异常处理与优化
3.1 异常处理机制
压缩解压过程中需处理多种异常:
public static void SafeCompress(string source, string dest)
{
try
{
if (string.IsNullOrWhiteSpace(source))
throw new ArgumentException("源路径不能为空");
if (!File.Exists(source) && !Directory.Exists(source))
throw new FileNotFoundException("源文件或目录不存在");
// 压缩逻辑...
}
catch (UnauthorizedAccessException ex)
{
Console.WriteLine($"权限不足: {ex.Message}");
}
catch (PathTooLongException ex)
{
Console.WriteLine($"路径过长: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"压缩失败: {ex.Message}");
}
}
3.2 性能优化策略
- 使用缓冲区减少IO操作
- 多线程处理大文件
- 根据文件类型选择压缩级别
// 多线程压缩示例
public static async Task ParallelCompressAsync(string sourceDir, string destZip)
{
var files = Directory.GetFiles(sourceDir);
var tasks = new List();
foreach (var file in files)
{
tasks.Add(CompressFileAsync(file, destZip));
}
await Task.WhenAll(tasks);
}
private static async Task CompressFileAsync(string file, string destZip)
{
// 实现异步压缩逻辑
await Task.Run(() =>
{
// 使用ZipArchive进行异步写入
});
}
四、完整帮助类实现
综合上述功能,构建完整的`CompressionHelper`类:
public static class CompressionHelper
{
// ZIP压缩(无密码)
public static void ZipCompress(string source, string dest)
{
if (Directory.Exists(source))
{
ZipFile.CreateFromDirectory(source, dest);
}
else if (File.Exists(source))
{
string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
Directory.CreateDirectory(tempDir);
File.Move(source, Path.Combine(tempDir, Path.GetFileName(source)));
ZipFile.CreateFromDirectory(tempDir, dest);
Directory.Delete(tempDir, true);
}
}
// ZIP解压
public static void ZipExtract(string source, string dest)
{
Directory.CreateDirectory(dest);
ZipFile.ExtractToDirectory(source, dest);
}
// 带密码的ZIP压缩(需SharpZipLib)
public static void ZipCompressWithPassword(string source, string dest, string password)
{
using (FileStream fsOut = File.Create(dest))
using (ZipOutputStream zipStream = new ZipOutputStream(fsOut))
{
zipStream.Password = password;
// ...实现同2.1节...
}
}
// GZIP压缩
public static void GzipCompress(string source, string dest)
{
using (FileStream sourceStream = File.OpenRead(source))
using (FileStream destStream = File.Create(dest))
using (GZipStream gzipStream = new GZipStream(destStream, CompressionLevel.Optimal))
{
sourceStream.CopyTo(gzipStream);
}
}
// 分卷压缩
public static void SplitCompress(string source, string baseName, long maxSize)
{
// ...实现同2.2节...
}
}
五、实际应用场景
5.1 日志归档系统
每日生成的日志文件可通过定时任务压缩存储:
public class LogArchiver
{
public void ArchiveDailyLogs(string logDir)
{
string today = DateTime.Now.ToString("yyyyMMdd");
string zipPath = Path.Combine(logDir, $"logs_{today}.zip");
var logFiles = Directory.GetFiles(logDir, "*.log")
.Where(f => Path.GetFileNameWithoutExtension(f).Contains(today.Substring(0, 6)));
foreach (var file in logFiles)
{
CompressionHelper.ZipCompress(file, zipPath);
File.Delete(file);
}
}
}
5.2 大文件传输优化
分卷压缩+断点续传实现:
public class FileTransferService
{
public void PrepareForTransfer(string filePath, string baseName, long chunkSize)
{
CompressionHelper.SplitCompress(filePath, baseName, chunkSize);
}
public void ReassembleFile(string baseName, string destPath)
{
var parts = Directory.GetFiles(Path.GetDirectoryName(baseName), $"{Path.GetFileNameWithoutExtension(baseName)}.part*.zip")
.OrderBy(f => f);
using (FileStream destStream = File.Create(destPath))
{
foreach (var part in parts)
{
using (FileStream sourceStream = File.OpenRead(part))
{
sourceStream.CopyTo(destStream);
}
}
}
}
}
六、测试与验证
6.1 单元测试示例
[TestClass]
public class CompressionTests
{
[TestMethod]
public void TestZipCompression()
{
string testDir = Path.Combine(Path.GetTempPath(), "TestZip");
Directory.CreateDirectory(testDir);
File.WriteAllText(Path.Combine(testDir, "test.txt"), "Hello World");
string zipPath = Path.Combine(Path.GetTempPath(), "test.zip");
CompressionHelper.ZipCompress(testDir, zipPath);
Assert.IsTrue(File.Exists(zipPath));
Directory.Delete(testDir, true);
}
[TestMethod]
public void TestGzipCompression()
{
string sourceFile = Path.Combine(Path.GetTempPath(), "test.dat");
byte[] data = new byte[1024];
new Random().NextBytes(data);
File.WriteAllBytes(sourceFile, data);
string gzipPath = Path.Combine(Path.GetTempPath(), "test.gz");
CompressionHelper.GzipCompress(sourceFile, gzipPath);
Assert.IsTrue(File.Exists(gzipPath));
File.Delete(sourceFile);
}
}
6.2 性能基准测试
使用BenchmarkDotNet对比不同压缩级别:
[MemoryDiagnoser]
public class CompressionBenchmark
{
private readonly byte[] _testData = new byte[1024 * 1024 * 10]; // 10MB数据
[Benchmark]
public void OptimalCompression()
{
using (MemoryStream ms = new MemoryStream())
using (GZipStream gzip = new GZipStream(ms, CompressionLevel.Optimal))
{
gzip.Write(_testData, 0, _testData.Length);
}
}
[Benchmark]
public void FastestCompression()
{
using (MemoryStream ms = new MemoryStream())
using (GZipStream gzip = new GZipStream(ms, CompressionLevel.Fastest))
{
gzip.Write(_testData, 0, _testData.Length);
}
}
}
七、常见问题解决方案
7.1 中文路径乱码问题
解决方案:指定编码方式
// 使用ZipArchive时设置编码
public static void ZipWithEncoding(string source, string dest)
{
using (FileStream fs = File.Create(dest))
using (ZipArchive archive = new ZipArchive(fs, ZipArchiveMode.Create))
{
// 添加文件时处理编码
foreach (var file in Directory.GetFiles(source))
{
string entryName = file.Replace(source, "").Trim('\\', '/');
archive.CreateEntryFromFile(file, entryName, CompressionLevel.Optimal);
}
}
}
7.2 大文件内存溢出
解决方案:使用流式处理
public static void StreamCompress(string source, string dest)
{
using (FileStream sourceStream = File.OpenRead(source))
using (FileStream destStream = File.Create(dest))
using (GZipStream gzipStream = new GZipStream(destStream, CompressionLevel.Optimal))
{
byte[] buffer = new byte[65536]; // 64KB缓冲区
int bytesRead;
while ((bytesRead = sourceStream.Read(buffer, 0, buffer.Length)) > 0)
{
gzipStream.Write(buffer, 0, bytesRead);
}
}
}
7.3 跨平台兼容性问题
解决方案:检测操作系统并调整路径分隔符
public static string NormalizePath(string path)
{
return Path.DirectorySeparatorChar == '\\'
? path.Replace('/', '\\')
: path.Replace('\\', '/');
}
关键词:C#压缩解压、ZIP格式、GZIP格式、密码保护、分卷压缩、SharpZipLib、性能优化、异常处理、流式处理、跨平台兼容
简介:本文详细介绍了C#中实现文件压缩解压的完整方案,涵盖ZIP/GZIP格式的基础操作,以及密码保护、分卷压缩等高级功能。通过代码示例展示了如何构建健壮的压缩解压帮助类,并提供了性能优化、异常处理和跨平台兼容的解决方案,适用于日志归档、大文件传输等实际开发场景。