65.9K
CodeProject 正在变化。 阅读更多。
Home

从远程 ZIP 存档中提取文件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.83/5 (37投票s)

2004年10月30日

6分钟阅读

viewsIcon

156358

downloadIcon

4835

本文介绍了一种访问存储在 Web 服务器上的 ZIP 存档部分的技术。

引言

本文解释了一种无需下载整个 ZIP 存档即可访问 ZIP 文件内部文件的方法。它可用于访问大型 ZIP 存档的部分文件,或仅下载存档的特定部分。它也适用于 OpenOffice 文档。

通过 Web 服务器提供存档文件部分的标准方法是使用服务器端的脚本,并且事实是单个部分以未压缩的方式发送;此解决方案完全在客户端进行,并使用 HTTP 协议的 Range 标头来检索 ZIP 存档的特定部分。

ZIP 存档的分析和解压缩是使用开源的 SharpZipLib 进行的。

架构

我们从 ZIP 存档的描述开始,这对于解释此技术的工作原理至关重要。ZIP 存档的组织方式如图所示;首先是压缩文件,每个文件前面都有一个本地头,最后是一个存储所有文件详细信息的中央目录。中央目录用于加速文件列表的读取,它存储在最后,因为这样 ZIP 存档就可以在流上创建。中央目录有一个头,然后是每个文件的记录,最后是一个结束标记。

ZIP File Format - diagram.png

中央目录中的一条记录包含本地头条目的偏移量,可用于提取指定的文件。不幸的是,必须读取本地头,因为中央目录的记录中没有关于本地头大小的信息。

有关 ZIP 文件格式的更多详细信息,可以 在此 获取。

该想法是利用 HTTP 协议的 Range 选项从中央目录中提取文件列表。当客户端应用程序请求存档中的文件时,只会传输压缩内容,解压缩在客户端进行。显然,当通信使用 HTTP 的 Keep-Alive 选项时,此技术效果最佳。

正如 RFC2616 (HTTP 1.1) 所述,请求标头中的 Range 选项指定了 HTTP 请求的第一个字节位置,还可以选择指定结束字节位置。如果第一个字节位置为负数,则偏移量从数据流的末尾开始计算。

Web 服务器响应 Range 请求时会返回“206 - Partial Content”响应,其中指定了发送回客户端的范围。响应标头选项 Content-range 报告返回的范围。HTTP 协议还能够处理每个 HTTP 请求中的多个范围请求。

实现

RemoteZip 类包含此技术的所有实现。最有趣的部分是搜索 ZIP 存档的记录结束,因为它位于文件末尾但长度可变。

要访问 Web 资源,我们使用 .NET Framework 的 HttpWebRequest 类,并且 AddRange 方法隐藏了使用 Range 选项的所有细节。当范围值为负数时,表示偏移量相对于末尾。

这是提取文件的 HTTP 请求示例

  1. 请求存档的最后 280 字节以查找中央目录的结束。从中央目录的结束中,获取中央目录的偏移量和长度;
  2. 加载中央目录及其所有条目信息。查找请求的文件并获取其在存档中的本地头偏移量;
  3. 请求一个从本地头开始的数据块,大小为本地头的最大大小加上压缩大小;从请求的数据中跳过本地头部分。然后,将解压缩的数据作为来自 Web 服务器的数据提供。

由于本地头的大小是动态的,我们请求 16+64K*2+compressedSize 字节。64K*2 是本地头动态部分的最大尺寸,但通常它是一个非常小的值。另一种方法是只下载本地头的静态部分,然后进行另一次请求以获取压缩数据,但这应该避免,因为会增加 HTTP 请求的开销。

对 SharpLibZip 的扩展

此技术被呈现为 SharpLibZip 的扩展。应该使用 RemoteZipFile 类而不是 ZipFile 类:它提供了存档条目的枚举,并且可以为每个条目获取一个 Stream 来读取数据。

用法

RemoteZipFile zip = new RemoteZipFile();
zip.Load(url);

foreach(ZipEntry ze in zip)
{
    // ...
}

ZipEntry ze = ...; 
Stream uncompressedStream = zip.GetInputStream(ze);

Stream 类的子类

一个有趣的实现细节是定义了两个包装其他流的 Stream 类

  1. NoCloseSubStream 是一个附加到另一个 Stream 的流,它在 Close 时自行分离。当用户完成对 Stream 的操作而不希望关闭它时,可以使用它;
  2. PartialInputStream 是一个流,它将流的一部分呈现为一个完整的流。当使用与原始流返回的大小不同的特定大小时解压缩来自 Web 服务器的数据时,可以使用它。

示例应用程序

在演示项目中,有一个 RemoteZip 应用程序可用于测试此技术。它访问远程 zip 文件并显示所有包含的文件以及相关信息。每个文件都可以保存到磁盘,或作为文本或图像预览(只要 .NET 能够识别图像数据)。

此快照显示了文本文件内部的预览

Sample Image - snapshot.jpg

在这种情况下,预览的是 OpenOffice 文档(.sxw)的图像

Sample Image - snapshot2.jpg

结论和未来工作

本文介绍了一种使用 HTTP Range 选项访问 ZIP 存档等大型资源部分的方法。该技术可用于恢复存档中的特定文件,例如 OpenOffice 文件或 JAR 的元数据,并且在不需要整个存档时非常高效。

探索 HTTP 协议中的多范围请求的可能性会非常有意义。这是一个来自 HTTP 规范的示例

 HTTP/1.1 206 Partial Content
   Date: Wed, 15 Nov 1995 06:25:24 GMT
   Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT
   Content-type: multipart/byteranges; boundary=THIS_STRING_SEPARATES

   --THIS_STRING_SEPARATES
   Content-type: application/pdf
   Content-range: bytes 500-999/8000

   ...the first range...
   --THIS_STRING_SEPARATES
   Content-type: application/pdf
   Content-range: bytes 7000-7999/8000

   ...the second range
   --THIS_STRING_SEPARATES--

实际上,它仅限于 HTTP,但也可以应用于本地文件和 FTP 文件。FTP 文件的情况很棘手,因为它使用 REST 命令从文件的特定位置开始传输,然后在传输完所需的字节数后中断下载。

参考文献

© . All rights reserved.