开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第29天,点击查看活动详情
本篇主要介绍使用C#自带的压缩类实现对Zip格式的文件或数据的解/压缩。
通过引用.NET自带的System.IO.Compression.FileSystem.dll、System.IO.Compression.dll(项目引用右键->添加引用->搜索System.IO.Compression.FileSystem添加),实现原生C#压缩、解压缩文件,读取压缩文档,并进行简单的ZipHelper、GZipHelper封装(后续)。
新建项目ZipUnZip,添加引用,using System.IO.Compression;。
目前所知7z压缩文件格式,需要引用第三方库,原生.NET未提供相关方法
压缩解压缩zip文件
压缩、解压缩的使用
System.IO.Compression命名空间下,ZipFile静态类提供压缩、解压缩文件夹的方法:
ZipFile.CreateFromDirectory(string sourceDirectoryName, string destinationArchiveFileName)从源目录创建压缩文件到目标地址(目标压缩文件名)ZipFile.ExtractToDirectory(string sourceArchiveFileName, string destinationDirectoryName)将源zip文件解压缩到目标目录中
默认不提供直接从一个文件创建压缩文档。
压缩时目标文档存在将会报错,因此可以提前判断,是否覆盖还是不压缩;解压缩时同样,如果源压缩文档不存在,或解压目标路径中存在同名文件,也都会报错。
- 压缩
// 压缩
var testPath = "test"; // 程序所在目录下的`test/`目录下有a.txt\b.txt\c.txt文件
var zipFile = "test.zip";
ZipFile.CreateFromDirectory(testPath, zipFile);
不能直接从文件创建压缩包,只能从文件夹创建压缩文件,否则报错:System.IO.IOException:目录名称无效。
var aTxt = @"test\a.txt";
var aTxtZip = "aTxt.zip";
// 报错:System.IO.IOException:目录名称无效。
ZipFile.CreateFromDirectory(aTxt, aTxtZip);
- 解压缩
解压缩刚创建的zipFile文件
// 解压缩
var extraPath = "unZipDir";
ZipFile.ExtractToDirectory(zipFile, extraPath);
读取压缩文档、解压缩指定文件
System.IO.Compression.dll提供了可以访问zip压缩文档的方法OpenRead/Open,获取到ZipArchive对象,可以读取zip压缩文件的内容,通ZipArchiveEntry对象的ExtractToFile方法解压其中的某个文件项。
如下,通过判断压缩档每项的文件名后置".txt",解压缩提取指定类型的文件。
string zipFile = @"test.zip";
string extractPath = @"extractSomeFile";
using (ZipArchive archive = ZipFile.OpenRead(zipFile))
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
if (entry.Name.EndsWith(".txt", StringComparison.OrdinalIgnoreCase))
{
entry.ExtractToFile(Path.Combine(extractPath, entry.FullName));
}
}
}
将指定文件添加到压缩包、向压缩包中某个文件写入内容
ZipArchive对象支持更新模式,从而向压缩包添加新文件(创建新项)。
ZipArchiveEntry 提供流对象的访问形式,从而实现向压缩档内的某个文件写入或添加新内容
var zipFile = "test.zip";
using (FileStream zipToOpen = new FileStream(zipFile, FileMode.Open))
{
using (ZipArchive archive = new ZipArchive(zipToOpen, ZipArchiveMode.Update))
{
ZipArchiveEntry readmeEntry = archive.CreateEntry("Readme.txt");
using (StreamWriter writer = new StreamWriter(readmeEntry.Open()))
{
writer.WriteLine("有关该压缩包的信息");
writer.WriteLine("========================");
}
}
}
压缩解压缩的帮助类封装ZipHelper
关于源文件和目标文件存在或不存在的处理,最好不要直接使用覆盖,而是一一确认【尤其压缩包中有多个文件与目标文件夹中的文件同名时】
/// <summary>
/// ZipHelper类,zip文件的压缩解压缩(UTF-8编码,如果需要指定其他编码,请使用ZipFile的原生方法)
/// </summary>
public static class ZipHelper
{
/// <summary>
/// Zip文件压缩,生成同名的.zip文件
/// </summary>
/// <param name="sourceDirOrFileName">源文件或目录</param>
/// <param name="isWrite">若zip文件存在是否覆盖</param>
/// <param name="compressionLevel">指定压缩级别,压缩速度还是大小</param>
/// <returns></returns>
public static void Compress(string sourceDirOrFileName,bool isWrite=false, CompressionLevel? compressionLevel = null)
{
Compress(sourceDirOrFileName, Path.Combine(File.Exists(sourceDirOrFileName) ? Path.GetDirectoryName(sourceDirOrFileName): Directory.GetParent(sourceDirOrFileName).FullName, $"{Path.GetFileNameWithoutExtension(sourceDirOrFileName)}.zip"), isWrite, compressionLevel);
}
/// <summary>
/// Zip文件压缩,生成指定文件名的.zip文件
/// </summary>
/// <param name="sourceDirOrFileName">源文件或目录</param>
/// <param name="destinationZipFileName">压缩包文件名(含.zip后缀的文件名)</param>
/// <param name="isWrite">若zip文件存在是否覆盖</param>
/// <param name="compressionLevel">指定压缩级别,压缩速度还是大小</param>
/// <returns></returns>
public static void Compress(string sourceDirOrFileName, string destinationZipFileName, bool isWrite = false, CompressionLevel? compressionLevel = null)
{
var sourceDirName = sourceDirOrFileName;
var isDeleteTempDir = false;
try
{
if (File.Exists(sourceDirOrFileName))
{
// 处理文件 创建随机文件夹并
var randomDir = Path.Combine( Path.GetDirectoryName(sourceDirOrFileName), Path.GetRandomFileName());
while (Directory.Exists(randomDir))
{
randomDir = Path.Combine(Path.GetDirectoryName(sourceDirOrFileName), Path.GetRandomFileName());
}
var di = Directory.CreateDirectory(randomDir);
di.Attributes = FileAttributes.Directory | FileAttributes.Hidden;
File.Copy(sourceDirOrFileName, Path.Combine(randomDir, Path.GetFileName(sourceDirOrFileName)));
sourceDirName = randomDir;
isDeleteTempDir = true;
}
var isCompress = true;
if (File.Exists(destinationZipFileName))
{
if (isWrite)//覆盖并压缩
{
File.Delete(destinationZipFileName);
}
else
{
isCompress = false;
}
}
if (isCompress)
{
if (compressionLevel.HasValue)
{
ZipFile.CreateFromDirectory(sourceDirName, destinationZipFileName, compressionLevel.Value,false);
}
else
{
ZipFile.CreateFromDirectory(sourceDirName, destinationZipFileName);
}
}
}
finally
{
if (isDeleteTempDir) // 删除创建的随机文件夹
{
if (Directory.Exists(sourceDirName)) Directory.Delete(sourceDirName, true);
}
}
}
/// <summary>
/// 向zip压缩档中添加文件或文件夹。暂未实现,基本思路是:遍历读取要添加的文件,同时在压缩档对象ZipArchive创建对应文件项,通过读取原文件流,写入新创建的ZipArchiveEntry。
/// 更正确的处理应该是,使用 ZipFileExtensions 的 ZipArchive 扩展方法CreateEntryFromFile(),直接从文件创建
/// </summary>
/// <param name="sourceDirOrFileName"></param>
/// <param name="destinationZipFileName"></param>
/// <param name="compressionLevel"></param>
[Obsolete("未实现",true)]
public static void CompressAddFile(string sourceDirOrFileName, string destinationZipFileName, CompressionLevel? compressionLevel = null){}
/// <summary>
/// 解压缩zip文件到当前文件夹
/// </summary>
/// <param name="sourceZipFileName">源zip文件</param>
/// <param name="isWrite">解压目标文件存在时是否覆盖,推荐false</param>
/// <returns></returns>
public static void Decompress(string sourceZipFileName,bool isWrite=false)
{
Decompress(sourceZipFileName,Path.Combine(Path.GetDirectoryName(sourceZipFileName),Path.GetFileNameWithoutExtension(sourceZipFileName)), isWrite);
}
/// <summary>
/// 解压缩zip文件
/// </summary>
/// <param name="sourceZipFileName">源zip文件</param>
/// <param name="destinationDirName">目标目录</param>
/// <param name="isWrite">解压目标文件存在时是否覆盖,推荐false</param>
/// <returns></returns>
public static void Decompress(string sourceZipFileName, string destinationDirName, bool isWrite = false)
{
try
{
ZipFile.ExtractToDirectory(sourceZipFileName, destinationDirName);
}
catch (IOException ex)
{
var isThrow = true;
if (isWrite)
{
// 判断目标文件是否存在
using (ZipArchive archive = ZipFile.OpenRead(sourceZipFileName)) // 需要添加System.IO.Compression(.dll)引用
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
if (File.Exists(Path.Combine(destinationDirName, entry.FullName)))
{
File.Delete(Path.Combine(destinationDirName, entry.FullName));
isThrow = false;
}
}
}
if (!isThrow) ZipFile.ExtractToDirectory(sourceZipFileName, destinationDirName);
}
if (isThrow) throw ex;
}
}
/// <summary>
/// 解压缩zip中指定模式的文件
/// </summary>
/// <param name="sourceZipFileName">源zip文件</param>
/// <param name="destinationDirName">目标目录</param>
/// <param name="zipEntryRegPattern">要解压缩的zip内文件名和路径符合的正则模式,比如,".txt$"解压缩txt后缀的文件</param>
/// <param name="isWrite">解压目标文件存在时是否覆盖,推荐false</param>
/// <returns></returns>
public static void Decompress(string sourceZipFileName, string destinationDirName,string zipEntryRegPattern, bool isWrite = false)
{
var existsFiles = new List<string>();
// 判断目标文件是否存在
using (ZipArchive archive = ZipFile.OpenRead(sourceZipFileName)) // 需要添加System.IO.Compression(.dll)引用
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
var m = Regex.Match(entry.FullName, zipEntryRegPattern);
if (m.Success)
{
if (File.Exists(Path.Combine(destinationDirName, entry.FullName)))
{
existsFiles.Add(entry.FullName);
}
}
}
if (existsFiles.Count>0)
{
if (!isWrite)
{
throw new Exception($"解压目录中存在同名文件,请确认后再解压缩。【{string.Join(";", existsFiles)}】");
}
}
foreach (ZipArchiveEntry entry in archive.Entries)
{
var m = Regex.Match(entry.FullName, zipEntryRegPattern);
if (m.Success)
{
entry.ExtractToFile(Path.Combine(destinationDirName, entry.FullName), true);
}
}
}
}
}
ZipFileExtensions 类中提供的扩展方法
ZipFileExtensions 类提供对ZipArchive和ZipArchiveEntry的扩展。
比如:
-
直接从文件创建
ZipArchiveEntry压缩归档项的ZipArchive扩展方法CreateEntryFromFile(ZipArchive, String, String)、CreateEntryFromFile(ZipArchive, String, String, CompressionLevel) -
解压某一个归档项到文件夹的ZipArchive扩展方法
ExtractToDirectory(ZipArchive, String)、ExtractToDirectory(ZipArchive, String, Boolean) -
上面已经使用到的ZipArchiveEntry提取到文件的扩展方法
ExtractToFile(ZipArchiveEntry, String)、``ExtractToFile(ZipArchiveEntry, String, Boolean)`
使用System.IO.Packaging实现对多个文件打包压缩
关于打包
参考
推荐
- adamhathcock/sharpcompress 【SharpCompress是一个完全由C#代码实现的库,MIT License】
- SharpCompress - a fully native C# library for RAR, 7Zip, Zip, Tar, GZip, BZip2
- BrotliStream Class
- C# Use Zip Archives without External Libraries