在项目开发中,除了对数据的展示更多的就是对文件的相关操作,例如文件的创建和删除,以及文件的压缩和解压。文件压缩的好处有很多,主要就是在文件传输的方面,文件压缩的好处就不需要赘述,因为无论是开发者,还是使用者对于文件压缩的好处都是深有体会。至于文件压缩的原理,在我的另一篇博客中有简单的介绍,在这里就不再做介绍,需要了解的可以查看。

.NET在System.IO.Compression命名空间中提供了GZip、Defalate两种压缩算法。今天我要介绍的一种压缩组件是DotNetZip组件。

一.DotNetZip组件概述:

在DotNetZip的自我介绍中号称是”DotNetZip是.NET最好的开源ZIP库“,至于是不是最好的压缩组件,在这里就不做评价,毕竟每个使用者的心态和工作环境不同,项目对组件的需求也不同,在选择组件的时候,就需要开发者自己衡量了。估计很多人还没有看到这里就开始在键盘上敲字吐槽了,标题是我借用官方对外的宣传口号,不用太在意这些细节。

DotNetZip - Zip和解压缩在C#,VB,任何.NET语言都可使用。DotNetZip是一个FAST,免费类库和用于操纵zip文件的工具集。 使用VB,C#或任何.NET语言轻松创建,解压缩或更新zip文件。DotNetZip在具有完整.NET Framework的PC上运行,并且还在使用.NET Compact Framework的移动设备上运行。在VB,C#或任何.NET语言或任何脚本环境中创建和读取zip文件。

DotNetZip组件的使用环境,毕竟软件的使用环境是每一个开发者都需要考虑的,这个世界没有绝对的好事,当然也没有绝对的坏事。接下来看一下其实用环境的说明吧:

  1.一个动态创建zip文件的Silverlight应用程序。

  2.一个ASP.NET应用程序,动态创建ZIP文件并允许浏览器下载它们。

  3.一个Windows服务,定期地为了备份和归档目的上拉一个目录。

  4.修改现有归档的WPF程序 - 重命名条目,从归档中删除条目或向归档中添加新条目。

  5.一个Windows窗体应用程序,用于为归档内容的隐私创建AES加密的zip存档。

  6.解压缩或拉链的SSIS脚本。

  7.PowerShell或VBScript中的一个管理脚本,用于执行备份和归档。

  8.WCF服务,接收作为附件的zip文件,并动态地将zip解压缩到流以进行分析。

  9.一个老式的ASP(VBScript)应用程序,通过COM接口为DotNetZIp生成一个ZIP文件。

  10.读取或更新ODS文件的Windows Forms应用程序。

  11.从流内容创建zip文件,保存到流,提取到流,从流读取。

  12.创建自解压档案。

DotNetZip是一个100%的托管代码库,可用于任何.NET应用程序 - 控制台,Winforms,WPF,ASP.NET,Sharepoint,Web服务应用程序等。 新的v1.9.1.6:Silverlight。 它还可以从脚本环境或具有COM功能的环境(如Powershell脚本,VBScript,VBA,VB6,PHP,Perl,Javascript等)中使用。 无论使用什么环境,DotNetZip生成的zip文件可与Windows资源管理器以及Java应用程序,在Linux上运行的应用程序完全互操作。

该组件设计简单,易于使用。 DotNetZip打包为一个单一的DLL,大小约400k。 它没有第三方依赖。 它是中等信任,因此可以在大多数托管商使用。 通过引用DLL来获取压缩。 该库支持zip密码,Unicode,ZIP64,流输入和输出,AES加密,多个压缩级别,自解压缩存档,跨区存档等。

以上的一些描述来自与官网,就不再吹捧这个组件了,在这里需要说明的是在组件的选择和使用上,主要取决与项目的实际情况。详情见:http://dotnetzip.codeplex.com/

二.DotNetZip相关核心类和方法解析:

由于下载的是DLL文件,还是采用.NET Reflector对DLL文件进行反编译,以此查看源代码。一下主要介绍一些类和方法,没有完全介绍,首先是由于篇幅所限,其实是完全没有必要,因为对于开发者而言,没有必要全部了解这些类,在实际的开发中,可以根据API进行对应的方法调用,这些技能应该是一个开发人员应该具备的。

1.ZipFile类的AddEntry()、Save()和IsZipFile()方法:

public ZipEntry AddEntry(string entryName, WriteDelegate writer)
{
ZipEntry ze = ZipEntry.CreateForWriter(entryName, writer);
if (this.Verbose)
{
this.StatusMessageTextWriter.WriteLine("adding {0}...", entryName);
}
return this._InternalAddEntry(ze);
}
public void Save()
{
try
{
bool flag = false;
this._saveOperationCanceled = false;
this._numberOfSegmentsForMostRecentSave = ;
this.OnSaveStarted();
if (this.WriteStream == null)
{
throw new BadStateException("You haven't specified where to save the zip.");
}
if (((this._name != null) && this._name.EndsWith(".exe")) && !this._SavingSfx)
{
throw new BadStateException("You specified an EXE for a plain zip file.");
}
if (!this._contentsChanged)
{
this.OnSaveCompleted();
if (this.Verbose)
{
this.StatusMessageTextWriter.WriteLine("No save is necessary....");
}
}
else
{
this.Reset(true);
if (this.Verbose)
{
this.StatusMessageTextWriter.WriteLine("saving....");
}
if ((this._entries.Count >= 0xffff) && (this._zip64 == Zip64Option.Default))
{
throw new ZipException("The number of entries is 65535 or greater. Consider setting the UseZip64WhenSaving property on the ZipFile instance.");
}
int current = ;
ICollection<ZipEntry> entries = this.SortEntriesBeforeSaving ? this.EntriesSorted : this.Entries;
foreach (ZipEntry entry in entries)
{
this.OnSaveEntry(current, entry, true);
entry.Write(this.WriteStream);
if (this._saveOperationCanceled)
{
break;
}
current++;
this.OnSaveEntry(current, entry, false);
if (this._saveOperationCanceled)
{
break;
}
if (entry.IncludedInMostRecentSave)
{
flag |= entry.OutputUsedZip64.Value;
}
}
if (!this._saveOperationCanceled)
{
ZipSegmentedStream writeStream = this.WriteStream as ZipSegmentedStream;
this._numberOfSegmentsForMostRecentSave = (writeStream != null) ? writeStream.CurrentSegment : ;
bool flag2 = ZipOutput.WriteCentralDirectoryStructure(this.WriteStream, entries, this._numberOfSegmentsForMostRecentSave, this._zip64, this.Comment, new ZipContainer(this));
this.OnSaveEvent(ZipProgressEventType.Saving_AfterSaveTempArchive);
this._hasBeenSaved = true;
this._contentsChanged = false;
flag |= flag2;
this._OutputUsesZip64 = new bool?(flag);
if ((this._name != null) && ((this._temporaryFileName != null) || (writeStream != null)))
{
this.WriteStream.Dispose();
if (this._saveOperationCanceled)
{
return;
}
if (this._fileAlreadyExists && (this._readstream != null))
{
this._readstream.Close();
this._readstream = null;
foreach (ZipEntry entry2 in entries)
{
ZipSegmentedStream stream2 = entry2._archiveStream as ZipSegmentedStream;
if (stream2 != null)
{
stream2.Dispose();
}
entry2._archiveStream = null;
}
}
string path = null;
if (File.Exists(this._name))
{
path = this._name + "." + Path.GetRandomFileName();
if (File.Exists(path))
{
this.DeleteFileWithRetry(path);
}
File.Move(this._name, path);
}
this.OnSaveEvent(ZipProgressEventType.Saving_BeforeRenameTempArchive);
File.Move((writeStream != null) ? writeStream.CurrentTempName : this._temporaryFileName, this._name);
this.OnSaveEvent(ZipProgressEventType.Saving_AfterRenameTempArchive);
if (path != null)
{
try
{
if (File.Exists(path))
{
File.Delete(path);
}
}
catch
{
}
}
this._fileAlreadyExists = true;
}
NotifyEntriesSaveComplete(entries);
this.OnSaveCompleted();
this._JustSaved = true;
}
}
}
finally
{
this.CleanupAfterSaveOperation();
}
}
public static bool IsZipFile(Stream stream, bool testExtract)
{
if (stream == null)
{
throw new ArgumentNullException("stream");
}
bool flag = false;
try
{
if (!stream.CanRead)
{
return false;
}
Stream @null = Stream.Null;
using (ZipFile file = Read(stream, null, null, null))
{
if (testExtract)
{
foreach (ZipEntry entry in file)
{
if (!entry.IsDirectory)
{
entry.Extract(@null);
}
}
}
}
flag = true;
}
catch (IOException)
{
}
catch (ZipException)
{
}
return flag;
}

2.Read()读取数据流:

private static ZipFile Read(Stream zipStream, TextWriter statusMessageWriter, Encoding encoding, EventHandler<ReadProgressEventArgs> readProgress)
{
if (zipStream == null)
{
throw new ArgumentNullException("zipStream");
}
ZipFile zf = new ZipFile {
_StatusMessageTextWriter = statusMessageWriter,
_alternateEncoding = encoding ?? DefaultEncoding,
_alternateEncodingUsage = ZipOption.Always
};
if (readProgress != null)
{
zf.ReadProgress += readProgress;
}
zf._readstream = (zipStream.Position == 0L) ? zipStream : new OffsetStream(zipStream);
zf._ReadStreamIsOurs = false;
if (zf.Verbose)
{
zf._StatusMessageTextWriter.WriteLine("reading from stream...");
}
ReadIntoInstance(zf);
return zf;
}

以上是对ZipFile类的一些方法的解析,提供了该组件的一些方法的源码,至于源码的解读上难度不是很大,至于该组件的API,可以在下载DLL文件后,可以直接查看相应的方法和属性,在这里就不做详细的介绍。

三.DotNetZip组件使用实例:

以上是对该组件的一些解析,接下来我们看看实例:

1.压缩ZIP文件:

        /// <summary>
/// 压缩ZIP文件
/// 支持多文件和多目录,或是多文件和多目录一起压缩
/// </summary>
/// <param name="list">待压缩的文件或目录集合</param>
/// <param name="strZipName">压缩后的文件名</param>
/// <param name="isDirStruct">是否按目录结构压缩</param>
/// <returns>成功:true/失败:false</returns>
public static bool CompressMulti(List<string> list, string strZipName, bool isDirStruct)
{
if (list == null)
{
throw new ArgumentNullException("list");
}
if (string.IsNullOrEmpty(strZipName))
{
throw new ArgumentNullException(strZipName);
}
try
{
//设置编码,解决压缩文件时中文乱码
using (var zip = new ZipFile(Encoding.Default))
{
foreach (var path in list)
{
//取目录名称
var fileName = Path.GetFileName(path);
//如果是目录
if (Directory.Exists(path))
{
//按目录结构压缩
if (isDirStruct)
{
zip.AddDirectory(path, fileName);
}
else
{
//目录下的文件都压缩到Zip的根目录
zip.AddDirectory(path);
}
}
if (File.Exists(path))
{
zip.AddFile(path);
}
}
//压缩
zip.Save(strZipName);
return true;
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}

2.解压ZIP文件:

        /// <summary>
/// 解压ZIP文件
/// </summary>
/// <param name="strZipPath">待解压的ZIP文件</param>
/// <param name="strUnZipPath">解压的目录</param>
/// <param name="overWrite">是否覆盖</param>
/// <returns>成功:true/失败:false</returns>
public static bool Decompression(string strZipPath, string strUnZipPath, bool overWrite)
{
if (string.IsNullOrEmpty(strZipPath))
{
throw new ArgumentNullException(strZipPath);
}
if (string.IsNullOrEmpty(strUnZipPath))
{
throw new ArgumentNullException(strUnZipPath);
}
try
{
var options = new ReadOptions
{
Encoding = Encoding.Default
};
//设置编码,解决解压文件时中文乱码
using (var zip = ZipFile.Read(strZipPath, options))
{
foreach (var entry in zip)
{
if (string.IsNullOrEmpty(strUnZipPath))
{
strUnZipPath = strZipPath.Split('.').First();
}
entry.Extract(strUnZipPath,overWrite
? ExtractExistingFileAction.OverwriteSilently
: ExtractExistingFileAction.DoNotOverwrite);
}
return true;
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}

3.得到指定的输入流的ZIP压缩流对象:

        /// <summary>
/// 得到指定的输入流的ZIP压缩流对象
/// </summary>
/// <param name="sourceStream">源数据流</param>
/// <param name="entryName">实体名称</param>
/// <returns></returns>
public static Stream ZipCompress(Stream sourceStream, string entryName = "zip")
{
if (sourceStream == null)
{
throw new ArgumentNullException("sourceStream");
}
var compressedStream = new MemoryStream();
long sourceOldPosition = ;
try
{
sourceOldPosition = sourceStream.Position;
sourceStream.Position = ;
using (var zip = new ZipFile())
{
zip.AddEntry(entryName, sourceStream);
zip.Save(compressedStream);
compressedStream.Position = ;
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
finally
{
try
{
sourceStream.Position = sourceOldPosition;
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
return compressedStream;
}

4.得到指定的字节数组的ZIP解压流对象:

        /// <summary>
/// 得到指定的字节数组的ZIP解压流对象
/// 当前方法仅适用于只有一个压缩文件的压缩包,即方法内只取压缩包中的第一个压缩文件
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static Stream ZipDecompress(byte[] data)
{
Stream decompressedStream = new MemoryStream();
if (data == null) return decompressedStream;
try
{
var dataStream = new MemoryStream(data);
using (var zip = ZipFile.Read(dataStream))
{
if (zip.Entries.Count > )
{
zip.Entries.First().Extract(decompressedStream);
// Extract方法中会操作ms,后续使用时必须先将Stream位置归零,否则会导致后续读取不到任何数据
// 返回该Stream对象之前进行一次位置归零动作
decompressedStream.Position = ;
}
}
}
catch(Exception ex)
{
throw new Exception(ex.Message);
}
return decompressedStream;
}

四.总结:

以上是对DotNetZip组件的一些解析和方法实例,至于这款组件是不是最好的.NET压缩组件,这个就不做评价。个人在选择组件的时候,首先考虑的是开源,其次是免费,最后再考虑效率和实用性,毕竟在国内的一些情况是所有开发者都清楚的(不提国外是由于我不知道国外的情况)。客户需要降低成本,并且组件要可以进行定制。不过个人认为收费应该是一种趋势,毕竟所有的产品都是需要人员进行维护和开发。以上的博文中有不足之处,还望多多指正。

.NET组件介绍系列:

一款开源免费的.NET文档操作组件DocX(.NET组件介绍之一)http://www.cnblogs.com/pengze0902/p/6122311.html

高效而稳定的企业级.NET Office 组件Spire(.NET组件介绍之二)http://www.cnblogs.com/pengze0902/p/6125570.html

最好的.NET开源免费ZIP库DotNetZip(.NET组件介绍之三)http://www.cnblogs.com/pengze0902/p/6124659.html

免费开源的DotNet二维码操作组件ThoughtWorks.QRCode(.NET组件介绍之四)http://www.cnblogs.com/pengze0902/p/6134506.html

免费开源的DotNet任务调度组件Quartz.NET(.NET组件介绍之五)http://www.cnblogs.com/pengze0902/p/6128558.html

免费高效实用的Excel操作组件NPOI(.NET组件介绍之六)http://www.cnblogs.com/pengze0902/p/6150070.html

最好的.NET开源免费ZIP库DotNetZip(.NET组件介绍之三)的更多相关文章

  1. Blazor Bootstrap 组件库浏览器通知组件介绍

    通知组件 通过浏览器API发送通知信息 , 桌面浏览器表现为右下角系统提示框弹出消息, 移动浏览器表现为弹窗或者到消息列表, blazor页面不在前台也可以通过本组件提醒用户. DEMO https: ...

  2. 开源免费且稳定实用的.NET PDF打印组件itextSharp(.NET组件介绍之八)

    在这个.NET组件的介绍系列中,受到了很多园友的支持,一些园友(如:数据之巅. [秦时明月]等等这些大神 )也给我提出了对应的建议,我正在努力去改正,有不足之处还望大家多多包涵.在传播一些简单的知识的 ...

  3. 免费开源的.NET多类型文件解压缩组件SharpZipLib(.NET组件介绍之七)

    前面介绍了六种.NET组件,其中有一种组件是写文件的压缩和解压,现在介绍另一种文件的解压缩组件SharpZipLib.在这个组件介绍系列中,只为简单的介绍组件的背景和简单的应用,读者在阅读时可以结合官 ...

  4. 免费开源的DotNet任务调度组件Quartz.NET(.NET组件介绍之五)

    很多的软件项目中都会使用到定时任务.定时轮询数据库同步,定时邮件通知等功能..NET Framework具有“内置”定时器功能,通过System.Timers.Timer类.在使用Timer类需要面对 ...

  5. 免费开源的DotNet二维码操作组件ThoughtWorks.QRCode(.NET组件介绍之四)

    在生活中有一种东西几乎已经快要成为我们的另一个电子”身份证“,那就是二维码.无论是在软件开发的过程中,还是在普通用户的日常中,几乎都离不开二维码.二维码 (dimensional barcode) , ...

  6. 一款开源免费的.NET文档操作组件DocX(.NET组件介绍之一)

    在目前的软件项目中,都会较多的使用到对文档的操作,用于记录和统计相关业务信息.由于系统自身提供了对文档的相关操作,所以在一定程度上极大的简化了软件使用者的工作量. 在.NET项目中如果用户提出了相关文 ...

  7. 免费高效实用的.NET操作Excel组件NPOI(.NET组件介绍之六)

    很多的软件项目几乎都包含着对文档的操作,前面已经介绍过两款操作文档的组件,现在介绍一款文档操作的组件NPOI. NPOI可以生成没有安装在您的服务器上的Microsoft Office套件的Excel ...

  8. vs2015下编译免费开源的jpeg库,ijg的jpeg.lib

    vs2015下编译免费开源的jpeg库,ijg的jpeg.lib 1. 去Independent JPEG Group官网www.ijg.org下载jpegsrc,我下载的版本是jpegsrc9c.z ...

  9. .NET 开源免费图表组件库,Winform,WPF 通用

    大家好, 我是等天黑, 今天给大家介绍一个功能完善, 性能强悍的图表组件库 ScottPlot, 当我第一次在 github 上看到这个库, 我看不懂,但我大受震撼, 这么好的项目当然要分享出来了. ...

随机推荐

  1. 对Castle Windsor的Resolve方法的解析时new对象的探讨

    依赖注入框架Castle Windsor从容器里解析一个实例时(也就是调用Resolve方法),是通过调用待解析对象的构造函数new一个对象并返回,那么问题是:它是调用哪个构造函数呢? 无参的构造函数 ...

  2. 如何正确使用日志Log

    title: 如何正确使用日志Log date: 2015-01-08 12:54:46 categories: [Python] tags: [Python,log] --- 文章首发地址:http ...

  3. 23种设计模式--观察者模式-Observer Pattern

    一.观察者模式的介绍      观察者模式从字面的意思上理解,肯定有两个对象一个是观察者,另外一个是被观察者,观察者模式就是当被观察者发生改变得时候发送通知给观察者,当然这个观察者可以是多个对象,在项 ...

  4. Vue + Webpack + Vue-loader 系列教程(2)相关配置篇

    原文地址:https://lvyongbo.gitbooks.io/vue-loader/content/ 使用预处理器 在 Webpack 中,所有的预处理器需要和一个相应的加载器一同使用.vue- ...

  5. 千呼万唤始出来,微软Power BI简体中文版官网终于上线了,中文文档也全了。。

    前几个月时间,研究微软Power BI技术,由于没有任何文档和资料,只能在英文官网瞎折腾,同时也发布了英文文档的相关文章:系列文章,刚好上周把文章发布完,结果简体中文版上线了.哈哈,心里有苦啊,早知道 ...

  6. 开发者接入 基本配置 服务器配置 out.aspx

    页面代码: 前段为默认的,什么都不用写,后台如下: 即可 来自为知笔记(Wiz)

  7. Golang 编写的图片压缩程序,质量、尺寸压缩,批量、单张压缩

    目录: 前序 效果图 简介 全部代码 前序: 接触 golang 不久,一直是边学边做,边总结,深深感到这门语言的魅力,等下要跟大家分享是最近项目 服务端 用到的图片压缩程序,我单独分离了出来,做成了 ...

  8. PHP获取上个月最后一天的一个容易忽略的问题

    正常来说,PHP是有一个很方便的函数可以获取上个月时间的 strtotime (PHP 4, PHP 5, PHP 7) strtotime - 将任何英文文本的日期时间描述解析为 Unix 时间戳 ...

  9. Web安全相关(五):SQL注入(SQL Injection)

    简介 SQL注入攻击指的是通过构建特殊的输入作为参数传入Web应用程序,而这些输入大都是SQL语法里的一些组合,通过执行SQL语句进而执行攻击者所要的操作,其主要原因是程序没有细致地过滤用户输入的数据 ...

  10. 「译」JUnit 5 系列:条件测试

    原文地址:http://blog.codefx.org/libraries/junit-5-conditions/ 原文日期:08, May, 2016 译文首发:Linesh 的博客:「译」JUni ...