.NET 压缩/解压文件
本文为大家介绍下.NET解压/压缩zip文件。虽然解压缩不是啥核心技术,但压缩性能以及进度处理还是需要关注下,针对使用较多的zip开源组件验证,给大家提供技术选型
目前了解到的常用技术方案有System.IO.Compression、SharpZipLib以及DotNetZip,下面我们分别介绍下使用以及性能
System.IO.Compression
如果你需要处理简单的ZIP压缩和解压任务,且不需要高级特性,建议使用System.IO.Compression。作为.NET标准库的一部分,不需要额外安装第三方库,而且会随着.NET平台的更新而更新
看下代码实现:
1 /// <summary>
2 /// 解压Zip文件
3 /// </summary>
4 /// <param name="filePath">zip文件路径</param>
5 /// <param name="outputFolder">解压目录</param>
6 /// <returns></returns>
7 public static void Decompress(string filePath, string outputFolder)
8 {
9 ZipFile.ExtractToDirectory(filePath, outputFolder);
10 }
11
12 /// <summary>
13 /// 压缩成Zip文件
14 /// </summary>
15 /// <param name="sourceFolder">文件目录</param>
16 /// <param name="zipFile">zip文件路径</param>
17 /// <param name="includeFolder">是否包含文件父目录(即sourceFolder本身)</param>
18 /// <returns></returns>
19 public static void Compress(string sourceFolder, string zipFile, bool includeFolder = true)
20 {
21 ZipFile.CreateFromDirectory(sourceFolder, zipFile, CompressionLevel.Fastest, includeFolder);
22 }
优点很明显,API简洁易懂,适用于简单的文件压缩和解压操作。当然提供的功能比较基础,缺乏一些高级特性,比如分卷压缩和加密,也提供不了操作详细进度
我们来测试下解压缩性能,找个zip文件,“智微工厂生产需要的固件及安装包.zip”文件大小847M,里面是如下结构有文件以及文件夹:

解压耗时:8484ms。再将解压后的文件夹压缩,耗时:28672ms
所以呢,比较简单的业务场景可以直接用这个方案。大家可以将这个方案放在公司通用基础技术组件里
SharpZipLib
支持多种压缩格式(如ZIP、TAR、GZIP、BZIP2等),并提供了高级功能如加密、分卷压缩等。icsharpcode/SharpZipLib: #ziplib is a Zip, GZip, Tar and BZip2 library written entirely in C# for the .NET platform. (github.com)
API设计可用性高,满足更多复杂定制化需求。社区里好多小伙伴在使用,开发历史久远、组件稳定性较高
引用下Nuget包SharpZipLib后,解压zip文件
获取压缩包压缩后的文件的大小,这里Size是压缩前大小,还有一个属性CompressedSize压缩后大小:
1 public static long GetZipFileTotalSize(string zipPath)
2 {
3 long totalSize = 0;
4 using FileStream fileStream = File.OpenRead(zipPath);
5 using ZipInputStream zipStream = new ZipInputStream(fileStream);
6 while (zipStream.GetNextEntry() is { } zipEntry)
7 {
8 totalSize += zipEntry.Size;
9 }
10
11 return totalSize;
12 }
解压Zip文件:
1 /// <summary>
2 /// 解压Zip文件
3 /// </summary>
4 /// <param name="zipFile">zip文件路径</param>
5 /// <param name="outputFolder">解压目录</param>
6 /// <param name="cancellationToken">取消操作</param>
7 /// <param name="progressChanged">解压进度回调</param>
8 /// <returns></returns>
9 public static async Task UnZipAsync(string zipFile, string outputFolder,
10 CancellationToken cancellationToken = default, Action<ZipProgress> progressChanged = null)
11 {
12 if (!File.Exists(zipFile))
13 {
14 throw new InvalidOperationException($"file not exist,{zipFile}");
15 }
16 var decompressLength = GetZipFileTotalSize(zipFile);
17 using FileStream fileStream = File.OpenRead(zipFile);
18 await Task.Run(() =>
19 {
20 using ZipInputStream zipStream = new ZipInputStream(fileStream);
21 long completedSize = 0;
22 while (zipStream.GetNextEntry() is { } zipEntry)
23 {
24 if (cancellationToken != default && cancellationToken.IsCancellationRequested)
25 {
26 cancellationToken.ThrowIfCancellationRequested();
27 }
28
29 if (zipEntry.IsDirectory)
30 {
31 string folder = Path.Combine(outputFolder, zipEntry.Name);
32 EnsureFolder(folder);
33 }
34 else if (zipEntry.IsFile)
35 {
36 var operatingSize = completedSize;
37 var zipEntryName = zipEntry.Name;
38 string fullEntryPath = Path.Combine(outputFolder, zipEntryName);
39 string dirPath = Path.GetDirectoryName(fullEntryPath);
40 EnsureFolder(dirPath);
41 //解压后的数据
42 long singleFileSize = WriteUnzipDataToFile(zipStream, fullEntryPath, partialFileSize =>
43 {
44 if (progressChanged == null)
45 {
46 return;
47 }
48 long currentSize = operatingSize + partialFileSize;
49 progressChanged.Invoke(new ZipProgress(currentSize, decompressLength, zipEntryName));
50 });
51 completedSize += singleFileSize;
52 }
53 }
54 }, cancellationToken);
55 }
解压进度能反馈详细的文件写入进度值。另外,这里有个文件夹判断处理,也是支持空文件夹的
Zip压缩,获取所有的文件夹/子文件夹、所有的文件,添加到ZipFile里保存:
1 /// <summary>
2 /// 压缩文件
3 /// </summary>
4 /// <param name="toZipDirectory">待压缩的文件夹</param>
5 /// <param name="destZipPath">Zip文件的保存路径</param>
6 /// <returns></returns>
7 public static bool Zip(string toZipDirectory, string destZipPath)
8 {
9 if (string.IsNullOrEmpty(destZipPath))
10 {
11 throw new ArgumentNullException(nameof(destZipPath));
12 }
13 if (!destZipPath.ToUpper().EndsWith(".ZIP"))
14 {
15 throw new ArgumentException("保存路径不是ZIP后缀", nameof(destZipPath));
16 }
17 if (!Directory.Exists(toZipDirectory))
18 {
19 throw new ArgumentException("待压缩的文件夹不存在", nameof(toZipDirectory));
20 }
21
22 var dirs = Directory.GetDirectories(toZipDirectory, "*", SearchOption.AllDirectories)
23 .Select(dir => PathUtils.GetRelativePath(toZipDirectory, dir));
24 var files = Directory.GetFiles(toZipDirectory, "*", SearchOption.AllDirectories).ToArray();
25 var destFiles = files.Select(file => PathUtils.GetRelativePath(toZipDirectory, file)).ToArray();
26 if (File.Exists(destZipPath))
27 {
28 File.Delete(destZipPath);
29 }
30 using (ZipFile zipFile = ZipFile.Create(destZipPath))
31 {
32 zipFile.BeginUpdate();
33 foreach (var dir in dirs)
34 {
35 zipFile.AddDirectory(dir);
36 }
37 for (int i = 0; i < files.Length; i++)
38 {
39 zipFile.Add(files[i], destFiles[i]);
40 }
41 zipFile.CommitUpdate();
42 }
43 return true;
44 }
值得一提的是,如有需要指定Zip压缩文件内的文件名以及文件路径,可以在文件时输入对应的压缩后路径定义,注意是指压缩包内的相对路径:
1 /// <summary>指定的文件压缩到对应的压缩文件中</summary>
2 /// <param name="files">待压缩的文件路径列表(绝对路径)</param>
3 /// <param name="destFiles">文件路径对应的压缩后路径列表,即压缩后压缩包内的文件路径</param>
4 /// <param name="destZipPath">Zip文件的保存路径</param>
5 public static bool Zip(List<string> files, List<string> destFiles, string destZipPath)
6 {
7 if (files.Count != destFiles.Count)
8 {
9 throw new ArgumentException($"{nameof(files)}与{nameof(destFiles)}文件列表数量不一致");
10 }
11 if (string.IsNullOrEmpty(destZipPath))
12 throw new ArgumentNullException(nameof(destZipPath));
13 using (ZipFile zipFile = ZipFile.Create(destZipPath))
14 {
15 zipFile.BeginUpdate();
16 for (int i = 0; i < files.Count; i++)
17 {
18 zipFile.Add(files[i], destFiles[i]);
19 }
20 zipFile.CommitUpdate();
21 }
22 return true;
23 }
SharpZipLib虽然功能丰富,但大家看上面的demo代码,接口搞的有点复杂、学习曲线较高
同样我们按上面测试操作,解压缩同一zip文件,解压耗时20719ms,压缩耗时102109ms。。。
DotNetZip
再看看DotNetZip,这个相对SharpZipLib,API设计的更友好、容易上手。官网是haf/DotNetZip.Semverd(github.com),它停止维护了。。。作者推荐大家去使用System.IO.Compression!好吧先忽略这个,尽管已不再积极维护,但稳定性、性能真的好,下面给大家列下使用demo和性能测试
Zip文件解压:
1 /// <summary>
2 /// 解压Zip文件
3 /// </summary>
4 /// <param name="zipFile">zip文件路径</param>
5 /// <param name="outputFolder">解压目录</param>
6 /// <param name="password">密码</param>
7 /// <param name="progressChanged">解压进度回调</param>
8 /// <returns></returns>
9 public static void UnZip(string zipFile, string outputFolder, string password, Action<ZipProgress> progressChanged)
10 {
11 if (!File.Exists(zipFile)) throw new InvalidOperationException($"file not exist,{zipFile}");
12 //获取文件解压后的大小
13 var totalZipSize = GetZipFileSize(zipFile);
14 long completedSize = 0L;
15 using (var zip = ZipFile.Read(zipFile))
16 {
17 zip.Password = password;
18 zip.ExtractProgress += (s, e) =>
19 {
20 if (e.EventType == ZipProgressEventType.Extracting_EntryBytesWritten)
21 {
22 var fileName = e.CurrentEntry.FileName;
23 if (e.BytesTransferred < e.TotalBytesToTransfer)
24 {
25 //单个文件解压中的进度
26 var operatingSize = completedSize + e.BytesTransferred;
27 progressChanged?.Invoke(new ZipProgress(operatingSize, totalZipSize, fileName));
28 }
29 else
30 {
31 //单个文件解压完全的进度
32 completedSize += e.TotalBytesToTransfer;
33 progressChanged?.Invoke(new ZipProgress(completedSize, totalZipSize, fileName));
34 }
35 }
36 };
37 zip.ExtractAll(outputFolder);
38 }
39 }
这里获取压缩后文件大小,与上面SharpZipLib的zipEntry.Size对应,取的是zipEntry.UncompressedSize
非常人性的提供了ExtractProgress事件进度,我们取的是Extracting_EntryBytesWritten类型,可以拿到细节进度。具体进度的处理看上方代码
因为反馈的是详细字节写入进度,所以间隔很短。。。1ms都能给你爆几次进度,尤其是大文件:

所以需要限制下回调Action触发,可以加个计时器限制单个文件的进度回调,如100ms内最多触发一次,下面是优化后的代码:
1 /// <summary>
2 /// 解压Zip文件
3 /// </summary>
4 /// <param name="zipFile">zip文件路径</param>
5 /// <param name="outputFolder">解压目录</param>
6 /// <param name="password">密码</param>
7 /// <param name="progressChanged">解压进度回调</param>
8 /// <returns></returns>
9 public static void UnZip(string zipFile, string outputFolder, string password,
10 Action<ZipProgress> progressChanged)
11 {
12 if (!File.Exists(zipFile)) throw new InvalidOperationException($"file not exist,{zipFile}");
13 //获取文件解压后的大小
14 var totalZipSize = GetZipFileSize(zipFile);
15 long completedSize = 0L;
16 using (var zip = ZipFile.Read(zipFile))
17 {
18 zip.Password = password;
19 var lastProgressTick = Environment.TickCount;
20 zip.ExtractProgress += (s, e) =>
21 {
22 if (e.EventType == ZipProgressEventType.Extracting_EntryBytesWritten)
23 {
24 var fileName = e.CurrentEntry.FileName;
25 if (e.BytesTransferred < e.TotalBytesToTransfer)
26 {
27 // 单个文件解压变化,限制间隔时间触发解压事件
28 if (Environment.TickCount - lastProgressTick < ProgressEventTick)
29 {
30 return;
31 }
32 lastProgressTick = Environment.TickCount;
33 //单个文件解压中的进度
34 var operatingSize = completedSize + e.BytesTransferred;
35 progressChanged?.Invoke(new ZipProgress(operatingSize, totalZipSize, fileName));
36 }
37 else
38 {
39 //重置计时器
40 lastProgressTick = Environment.TickCount;
41 //单个文件解压完全的进度
42 completedSize += e.TotalBytesToTransfer;
43 progressChanged?.Invoke(new ZipProgress(completedSize, totalZipSize, fileName));
44 }
45 }
46 };
47 zip.ExtractAll(outputFolder);
48 }
49 }
再看看Zip压缩:
1 public static void Zip(string sourceFolder, string destZipFile, string password,
2 Action<ZipProgress> zipProgressAction)
3 {
4 if (string.IsNullOrEmpty(destZipFile)) throw new ArgumentNullException(nameof(destZipFile));
5 if (!destZipFile.ToUpper().EndsWith(".ZIP")) throw new ArgumentException("保存路径不是Zip文件", destZipFile);
6 if (File.Exists(destZipFile)) File.Delete(destZipFile);
7
8 using (var zipFile = new ZipFile())
9 {
10 // 设置压缩进度事件处理程序
11 zipFile.SaveProgress += (sender, e) =>
12 {
13 if (e.EventType == ZipProgressEventType.Saving_AfterWriteEntry)
14 zipProgressAction?.Invoke(new ZipProgress(e.EntriesSaved, e.EntriesTotal, e.CurrentEntry.FileName));
15 };
16 zipFile.AddDirectory(sourceFolder);
17 zipFile.Password = password;
18 zipFile.Save(destZipFile);
19 }
20 }
如果不考虑加密、压缩进度,DotNetZip压缩zip文件只需要几行代码,所以是相当的易学易用、入手快
还是同一个847M的zip文件,测试下解压缩性能,解压11907ms,压缩耗时16282ms,用数据说话性能强不强
用表格把这三个方案的对比列下:

所以如果你需要处理简单的ZIP压缩和解压任务,且不需要高级特性,建议使用System.IO.Compression
需要考虑解压缩性能,推荐使用DotNetZip。至于停止维护的状况可以忽然,有BUG大家可以在公司内或者github维护下这个组件代码
.NET 压缩/解压文件的更多相关文章
- 通过SharpZipLib来压缩解压文件
在项目开发中,一些比较常用的功能就是压缩解压文件了,其实类似的方法有许多 ,现将通过第三方类库SharpZipLib来压缩解压文件的方法介绍如下,主要目的是方便以后自己阅读,当然可以帮到有需要的朋友更 ...
- .NET使用ICSharpCode.SharpZipLib压缩/解压文件
SharpZipLib是国外开源加压解压库,可以方便的对文件进行加压/解压 1.下载ICSharpCode.SharpZipLib.dll,并复制到bin目录下 http://www.icsharpc ...
- huffman压缩解压文件【代码】
距离上次写完哈夫曼编码已经过去一周了,这一周都在写huffman压缩解压,哎,在很多小错误上浪费了很多时间调bug.其实这个程序的最关键部分不是我自己想的,而是借鉴了某位园友的代码,但是,无论如何,自 ...
- 【转载】.NET压缩/解压文件/夹组件
转自:http://www.cnblogs.com/asxinyu/archive/2013/03/05/2943696.html 阅读目录 1.前言 2.关于压缩格式和算法的基础 3.几种常见的.N ...
- C#使用SharpZipLib压缩解压文件
#region 加压解压方法 /// <summary> /// 功能:压缩文件(暂时只压缩文件夹下一级目录中的文件,文件夹及其子级被忽略) /// </summary> // ...
- linux压缩解压文件
首先进入文件夹 cd /home/ftp2/1520/web 压缩方法一:压缩web下的888.com网站 zip -r 888.com.zip888.com 压缩方法二:将当前目录下的所有文件和文件 ...
- Freebsd下压缩解压文件详解
压缩篇: 把/usr/webgames目录下的文件打包.命名为bak.tar.gz 放到/usr/db-bak目录里 下面命令可以在任意目录执行.无视当前目录和将要存放文件的目录.tar -zcvf ...
- 跨平台的zip文件压缩处理,支持压缩解压文件夹
根据minizip改写的模块,需要zlib支持 输出的接口: #define RG_ZIP_FILE_REPLACE 0 #define RG_ZIP_FILE_APPEND 1 //压缩文件夹目录, ...
- tar压缩解压文件
查看visualization1.5.tar.gz 压缩包里面的内容: $ tar -tf visualization1.5.tar.gz 解压指定文件JavascriptVisualRelease/ ...
- Ubuntu下压缩解压文件
一般来说ubuntu 下带有tar 命令,可以用来解压和压缩之用.但是我们经常要与win下用户打交道,所以要安装一些解压工具如:rar zip 等命令. 如果要需要用到zip工具那么可以: sudo ...
随机推荐
- Git常用命令汇总以及其它相关操作
--文件目录操作命令 1 mkdir * 创建一个空目录 *指目录名 2 pwd 显示当前目录的路径. 3 cat * 查看*文件内容 4 git rm * 删除**文件 --git初始化操作 1 g ...
- 【VMware VCF】VMware Cloud Foundation Part 03:准备 Excel 参数表。
VMware Cloud Foundation 使用 VMware Cloud Builder 工具完成自动化以及标准化的部署,除了要准备必须的用于部署管理域并运行管理相关组件的 ESXi 主机以外, ...
- CCF 有趣的数
问题描述: 试题编号: 201312-4 试题名称: 有趣的数 时间限制: 1.0s 内存限制: 256.0MB 问题描述: 问题描述 我们把一个数称为有趣的,当且仅当: 1. 它的数字只包含0, 1 ...
- php 后台执行进程
一些业务需要长期在后台执行进程 下面用thinkphp5.1做演示 在command文件夹下建立进程 cd 到 lunar_php项目根目录 执行 php think hello 如果想后台执行 no ...
- 《Programming from the Ground Up》阅读笔记:p88-p94
<Programming from the Ground Up>学习第5天,p88-p94总结,总计7页. 一.技术总结 1.touppercase.s #PURPOSE: This pr ...
- 【云服务器】记录使用腾讯云服务器搭建个人blog网站-【1】服务器配置
服务器购买 第一次写博客,写的不好请见谅 腾讯云教育活动 配置还行,能搭建个网站了果断下单 选择系统 缺点(对我来说):参考于:人生不开窍:Windows Server各版本差异 不能安装window ...
- python Selenium 不要混合隐式和显式等待
警告:不要混合隐式和显式等待.这样做可能会导致不可预测的等待时间.例如,设置10秒的隐式等待和15秒的显式等待可能会导致20秒后发生超时 Warning: Do not mix implicit an ...
- docker无法安装而需要的换源需求
docker无法安装镜像而需要的换源需求: 报错信息 (超时连接) 第一步: 登录阿里云:https://www.aliyun.com/ 第二步: 进入阿里云镜像加速: 点击"控制台&quo ...
- 【MongoDB】Re05 分片集群(Win平台搭建)
分片副本集1 (3实例) 主1 从1 裁1 分片副本集2 (3实例) 主1 从1 裁1 配置副本集(3实例) 主1 从2 路由(2配置) 用Windows平台搭建 配置目录设置: ├─config ...
- 【H5】06 网页架构
摘自: https://developer.mozilla.org/zh-CN/docs/Learn/HTML/Introduction_to_HTML/%E6%96%87%E4%BB%B6%E5%9 ...