日期:2011-12-13  浏览次数:20364 次

本文假设您熟悉 C# 和 Windows 窗体

下载本文的代码: ZipCompression.exe (150KB)

摘要

在存储文件或者通过网络发送文件时,使用 Zip 压缩可以节省空间和网络带宽。此外,还不会丢失经过 Zip 的文件夹的目录结构,这使其成为非常有用的压缩方案。C# 语言不具有任何使您可以操纵 Zip 文件的类,但是由于面向 .NET 的语言可以共享类实现,并且 J# 在 java.util.zip 命名空间中公开了类,因此您可以在 C# 代码中使用这些类。本文将解释如何使用 Microsoft J# 类库创建能够压缩和解压缩 Zip 文件的 C# 应用程序。它还将介绍 J# 运行库的其他一些可以从任何 .NET 兼容语言中使用以节省某些编码工作的独特部分。



本页内容
Zip 是一种受人欢迎的数据传输和存储标准,因为它可以节省磁盘空间和网络带宽。典型的文本和数据库文件可以被压缩至它们原始大小的 10%。即使二进制文件不能进行同样的压缩,通常也可以获得 50% 的压缩比。

Zip 文件的一个附加优点是单个文件可以包含多个文件,同时可以保留目录结构。这使您可以发送附加到电子邮件消息中的完整目录树,并且让收件人恢复原始文件结构。

Zip 数据格式是开放的,并且不会涉及专利权或其他法律问题。开发人员可以自由地创建操纵 Zip 文件的应用程序,以及使用低级别 Zip 压缩算法来暂时减小他们自己的自定义数据的大小。Zip 数据规范的作者在名为 zlib 的库 (http://www.gzip.org/zlib) 中向开发人员提供压缩和解压缩算法。Java 平台在 Java 开发工具包 (JDK) 的版本 1.1 中采用了该库,以构成 Java 存档 (JAR) 文件格式的基础,因此从 JDK 版本 1.1 开始,标准 Java 语言 API 就包含了操纵 Zip 文件所需的类。可以在 java.util.zip 命名空间下找到这些类。

Zip 文件和 C#


我希望在用 C# 编写的应用程序中使用 Zip 压缩。遗憾的是,Microsoft.NET Framework 当前不包含任何用于操纵 Zip 文件的类。但是,我的确找到了几个与 Zip 压缩有关的产品。例如,#ziplib(以前称为 NZipLib,http://www.icsharpcode.net/OpenSource/SharpZipLib/default.asp)是 zlib 库到 C# 的移植产品。它的许可证允许开发人员在封闭源代码的商业应用程序中包含该库。但是,在 MSDN Magazine 付印之时,#ziplib 尚处于预发布状态(版本 0.31)。

另外一个解决方案是使用非托管 zlib 作为 Windows DLL 并且为其编写必要的 Interop 包装,但是由于压缩涉及到在每个函数调用期间到处传递大量数据,因此编写 Interop 包装以获得最佳性能将是一个困难的过程。尽管可以使用其他库,但它们不是免费的。

返回页首
解决方案


.NET Framework 的设计考虑了语言互操作性。可以从任何实现了必要功能的 .NET 兼容编程语言中正确地使用所有遵循某些特定规则的托管组件。互操作性所需的规则和语言功能集称为公共语言规范 (CLS)。

Microsoft 实现的所有 .NET 语言编译器都是符合 CLS 的,其中包括 Microsoft Visual J# .NET — 一种供希望在 Microsoft .NET Framework 上生成应用程序和服务的 Java 语言开发人员使用的开发工具。(Visual J# .NET 是由 Microsoft 独立开发的。它没有经过 Sun Microsystems, Inc. 的认可和批准)这就是为什么可以在用 J# 编写的 Windows 窗体和 ASP.NET 应用程序中使用 .NET Framework 类的原因。

正像您将在本文稍后看到的那样,J# 运行库公开的某些类实际上并不符合 CLS,但是您仍然可以从其他语言中访问大多数 J# 类,以便使用 .NET Framework 未实现的特定功能。由于 J# 实现了 JDK 版本 1.1.4,因此丝毫不会令人感到意外的是,开发人员可以通过 J# 运行库访问 java.util.zip 命名空间。在本文的下一部分中,我将介绍一个用 C# 编写的应用程序,它使用 java.util.zip 类压缩和解压缩 Zip 文件,以便在本地节省空间以及在网络中节省带宽。

本文中的所有示例代码都是用 Microsoft Visual Studio 2002 和 J# 运行库版本 1.0(参见位于本文顶部的链接)开发的。

返回页首
SharpZip


我用 C# 编写了本文随附的示例应用程序之一 SharpZip。它是一个用于处理 Zip 文件的简化实用工具,通过它可以创建 Zip 文件,或者打开现有的 Zip 文件以解压缩、附加和删除文件(参见图 1)。



图 1 SharpZip 应用程序


在查看代码之前,您需要确保在系统中正确安装了 J# 运行库。无需安装完整的 Visual J# .NET 产品。您可以只下载并安装 J# 1.0 Redistributable Package,它可以从 http://msdn.microsoft.com/vjsharp/downloads/howtoget.asp 获得。

Java.util.zip 命名空间在 vjslib.dll 程序集中实现。该程序集位于 C:\WINNT\Microsoft Visual JSharp .NET\Framework\v1.0.4205\ 目录中(您需要将 WINNT 替换为实际的 Windows 目录)。

在项目中包含对 vjslib.dll 的引用时,可以开始从代码中使用 J# 命名空间并且用对象浏览器浏览 JDK 命名空间(参见图 2)。重要的类包括 java.util.zip.ZipFile、java.util.zip.ZipEntry 和 java.util.zip.ZipOutputStream。这些类显示在图 3 中,通过它们可以在文件级别操纵 Zip 文件。



图 2 对象浏览器中的命名空间


在使用本文中概述的方法时,方法名称在您看来可能是陌生的,这是因为 Java 用于标识符(除类和接口外)的命名约定与在 C# 中使用的命名约定有所不同。在 Java 中,命名空间和方法名称是使用低级大小写混合编写的,其中第一个字母小写,其余单词为首字母大写,如“nextElement”所示。但是,我肯定您会掌握这种方法的。

返回页首
枚举 Zip 条目


Java.util.zip.ZipFile 类的 entries 方法返回一个实现 java.util.Enumeration 接口的对象。然后,应用程序遍历枚举,以检索表示 Zip 文件中的各个条目的 ZipEntry 实例。ZipEntry 类将公开所有需要的信息,例如,文件名、压缩方法、时间戳、原始大小和压缩大小等等(参见图 4)。

请注意,尽管 java.util.Enumeration 接口类似于 System.Collections.IEnumerator 接口,但 Java 枚举器在您通过调用 nextElement 检索当前对象时前进至下一个元素,而 .NET 枚举器当您在 MoveNext 调用中检查更多元素的可用性时前进。另一个重要差异是 Enumeration 接口不提供用于重新启动遍历的方法。

.NET 枚举器的一个优点是您可以多次访问当前元素。另一方面,Java 枚举器使您可以多次检查完成情况,但是这在大多数情况下不是非常有用。Java 和 .NET 枚举器都经过了良好的设计,能够防止您在枚举循环内部忘记前进至下一个元素。

我决定编写一个用于包装 Java 枚举器的类,以便我可以将 C# foreach 语句与它们一起使用。我将该类命名为 EnumerationAdapter。我通过再次调用能够返回 Java 枚举器的方法来模拟 Reset 方法。为此,包装类构造函数采用 java.util.Enumeration 接口的委托作为参数,而不是 java.util.Enumeration 接口本身作为参数。

返回页首
解压缩 Zip 文件


SharpZip 应用程序在解压缩文件时所做的第一件事情,是提示用户指定应当在其中创建文件的目录。您可能已经注意到,应用程序显示了“Browse for Folder”对话框。我倾向于使用 System.Windows.Forms.Design.FolderNameEditor.Fol