详解最好的.NET开源免费ZIP库DotNetZip(.NET组件介绍之三)

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

.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 = 0;
  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 = 0;
   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 : 1;
    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 = 0;
   try
   {
    sourceOldPosition = sourceStream.Position;
    sourceStream.Position = 0;
    using (var zip = new ZipFile())
    {
     zip.AddEntry(entryName, sourceStream);
     zip.Save(compressedStream);
     compressedStream.Position = 0;
    }
   }
   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 > 0)
     {
      zip.Entries.First().Extract(decompressedStream);
      // Extract方法中会操作ms,后续使用时必须先将Stream位置归零,否则会导致后续读取不到任何数据
      // 返回该Stream对象之前进行一次位置归零动作
      decompressedStream.Position = 0;
     }
    }
   }
   catch(Exception ex)
   {
    throw new Exception(ex.Message);
   }
   return decompressedStream;
  }

四.总结:

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

(0)

相关推荐

  • ASP.Net MVC_DotNetZip简单使用方法,解决文件压缩的问题

    准备工作: 在vs工具栏中找到NuGet 下载DotNetZip 现在就可以使用DotNetZip强大的类库了,在这里我给出一些简单的使用. public ActionResult Export() { using (ZipFile zip = new ZipFile(System.Text.Encoding.Default)) { zip.AddFile(Server.MapPath("~/Img/2.png"), "Images"); zip.AddFile(S

  • c#使用DotNetZip封装类操作zip文件(创建/读取/更新)实例

    下载地址在这里:http://dotnetzip.codeplex.com/ 下载到的包里有很多个dll文件,一般引用Ionic.Zip.dll就可以: 然后引用这个命名空间: using Ionic.Zip; 以下是我自己封装的一个类: 复制代码 代码如下: /// <summary>    /// Zip操作基于 DotNetZip 的封装    /// </summary>    public static class ZipUtils    {        /// <

  • 详解最好的.NET开源免费ZIP库DotNetZip(.NET组件介绍之三)

    在项目开发中,除了对数据的展示更多的就是对文件的相关操作,例如文件的创建和删除,以及文件的压缩和解压.文件压缩的好处有很多,主要就是在文件传输的方面,文件压缩的好处就不需要赘述,因为无论是开发者,还是使用者对于文件压缩的好处都是深有体会.至于文件压缩的原理,在我的另一篇博客中有简单的介绍,在这里就不再做介绍,需要了解的可以查看. .NET在System.IO.Compression命名空间中提供了GZip.Defalate两种压缩算法.今天我要介绍的一种压缩组件是DotNetZip组件. 一.D

  • 详解DevEco Studio项目构建讲解、编写页面、布局介绍、页面跳转

    首先要知道鸿蒙的APP是怎么构成的?   HarmonyOS的应用软件包以APP Pack(Application Package)形式发布,它是由一个或多个HAP(HarmonyOS Ability Package)以及描述每个HAP属性的pack.info组成.HAP是Ability的部署包,HarmonyOS应用代码围绕Ability组件展开. 一个HAP是由代码.资源.第三方库及应用配置文件组成的模块包,可分为entry和feature两种模块类型,如下图所示. 一.项目目录 首先来看一

  • 详解pycharm自动import所需的库的操作方法

    快捷键命令 win: Alt + Enter mac: option + Enter 演示操作(以mac为例) 1.未导入包时情景 未导入views包,会有红色下划线标记 2.按option + Enter键唤起菜单 3.选择导入包(Import this name) 4.选择包的地址 5.导入成功,红色下划线消失 到此这篇关于详解pycharm自动import所需的库的操作方法的文章就介绍到这了,更多相关pycharm自动import所需的库内容请搜索我们以前的文章或继续浏览下面的相关文章希望

  • 详解如何在vue+element-ui的项目中封装dialog组件

    1.问题起源 由于 Vue 基于组件化的设计,得益于这个思想,我们在 Vue 的项目中可以通过封装组件提高代码的复用性.根据我目前的使用心得,知道 Vue 拆分组件至少有两个优点: 1.代码复用. 2.代码拆分 在基于 element-ui 开发的项目中,可能我们要写出一个类似的调度弹窗功能,很容易编写出以下代码: <template> <div> <el-dialog :visible.sync="cnMapVisible">我是中国地图的弹窗&l

  • 详解MVC如何使用开源分页插件(shenniu.pager.js)

    最近比较忙,前期忙公司手机端接口项目,各种开发+调试+发布现在几乎上线无问题了:虽然公司项目忙不过在期间抽空做了两件个人觉得有意义的事情,一者使用aspnetcore开发了个人线上项目(要说线上其实只能ip访问,没有域名哈哈),其架构组成由:aspnetcore1.0.0+redis+ postgressql+TaskMainForm服务,这个项目在后期会开源出来供大家分享学习,站点地址点这里心声网:一者是目前正在做的后台管理框架一叶子,现目前刚好吧js分页插件shenniu.pager.js写

  • 详解React-Native全球化多语言切换工具库react-native-i18n

    开篇啰嗦–阶段感悟 最近2 -3个月基本都因为一些私事没怎么系统的工作和学习,途中看了几天Kotlin的东西写了些demo并且整了个小项目,但是整体状态不是很好,这些天看到些95后码农的强势细思极恐. 现在大多数醒来就已经是中午,起得早去一下健身房,起的晚就家里宅一天.公司有事或者有其他家事就去协调/沟通/处理下,整个人感觉都提前进入养老状态(当然这个锅有一半是沉迷王者荣耀不可自拔,不太好) 最近项目上基本没啥事情了,然后让手下的小伙伴们对之前做的一些内容进行二次封装,然后他们引用了一个第三方国

  • 详解windows下vue-cli及webpack 构建网站(三)使用组件

    1.本文章是建立在<windows下vue-cli及webpack 构建网站(一)环境安装>和<windows下vue-cli及webpack 构建网站(一)导入bootstrap样式>两篇文章之上的. 2.在src\components文件夹下面新建两个组件,分别为 header.vue.footer.vue,打开header.vue文件粘贴下面的代码作为网站的头部. <template> <!-- Fixed navbar --> <nav cla

  • 详解docker使用阿里云Docker镜像库加速(修订版)

    官方镜像下载实在是慢,于是开通了阿里云开发者帐号, 阿里的文档是错误的, 复制代码 代码如下: sudo sed -i "s|ExecStart=/usr/bin/docker daemon|ExecStart=/usr/bin/docker daemon --registry-mirror=https://pee6w651.mirror.aliyuncs.com|g" /etc/systemd/system/docker.service 这一句改为 复制代码 代码如下: sudo s

  • 详解python如何调用C/C++底层库与互相传值

    前言 开发环境: Centos 7 + Python 3.5.1 + Qt Creator(只是使用Qt Creator编译而已,并没有使用QT的任何库) Python调用C/C++库,我现在能做到的有两种方式 1.extern "C" 导出(互相传值比较麻烦,不建议使用这种方式): 将C/C++库做成和平常一样的DLL和或者.so,比如: //.h文件 #include <Python.h> //.cpp文件 //C/C++ my.so 或者my.dll enter &q

  • 详解IOS点击空白处隐藏键盘的几种方法介绍

    IOS7 点击空白处隐藏键盘的几种方法,具体如下: iOS开发中经常要用到输入框,默认情况下点击输入框就会弹出键盘,但是必须要实现输入框return的委托方法才能取消键盘的显示,对于用户体验来说很不友好,我们可以实现点击键盘以外的空白区域来将键盘隐藏,以下我总结出了几种隐藏键盘的方法: 首先说明两种可以让键盘隐藏的Method: 1.[view endEditing:YES]  这个方法可以让整个view取消第一响应者,从而让所有控件的键盘隐藏. 2.[textFiled resignFirst

随机推荐