从远程 ZIP 存档中提取文件






4.83/5 (37投票s)
2004年10月30日
6分钟阅读

156358

4835
本文介绍了一种访问存储在 Web 服务器上的 ZIP 存档部分的技术。
引言
本文解释了一种无需下载整个 ZIP 存档即可访问 ZIP 文件内部文件的方法。它可用于访问大型 ZIP 存档的部分文件,或仅下载存档的特定部分。它也适用于 OpenOffice 文档。
通过 Web 服务器提供存档文件部分的标准方法是使用服务器端的脚本,并且事实是单个部分以未压缩的方式发送;此解决方案完全在客户端进行,并使用 HTTP 协议的 Range 标头来检索 ZIP 存档的特定部分。
ZIP 存档的分析和解压缩是使用开源的 SharpZipLib 进行的。
架构
我们从 ZIP 存档的描述开始,这对于解释此技术的工作原理至关重要。ZIP 存档的组织方式如图所示;首先是压缩文件,每个文件前面都有一个本地头,最后是一个存储所有文件详细信息的中央目录。中央目录用于加速文件列表的读取,它存储在最后,因为这样 ZIP 存档就可以在流上创建。中央目录有一个头,然后是每个文件的记录,最后是一个结束标记。
中央目录中的一条记录包含本地头条目的偏移量,可用于提取指定的文件。不幸的是,必须读取本地头,因为中央目录的记录中没有关于本地头大小的信息。
有关 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 请求示例
- 请求存档的最后 280 字节以查找中央目录的结束。从中央目录的结束中,获取中央目录的偏移量和长度;
- 加载中央目录及其所有条目信息。查找请求的文件并获取其在存档中的本地头偏移量;
- 请求一个从本地头开始的数据块,大小为本地头的最大大小加上压缩大小;从请求的数据中跳过本地头部分。然后,将解压缩的数据作为来自 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 类
NoCloseSubStream
是一个附加到另一个 Stream 的流,它在Close
时自行分离。当用户完成对 Stream 的操作而不希望关闭它时,可以使用它;PartialInputStream
是一个流,它将流的一部分呈现为一个完整的流。当使用与原始流返回的大小不同的特定大小时解压缩来自 Web 服务器的数据时,可以使用它。
示例应用程序
在演示项目中,有一个 RemoteZip 应用程序可用于测试此技术。它访问远程 zip 文件并显示所有包含的文件以及相关信息。每个文件都可以保存到磁盘,或作为文本或图像预览(只要 .NET 能够识别图像数据)。
此快照显示了文本文件内部的预览
在这种情况下,预览的是 OpenOffice 文档(.sxw)的图像
结论和未来工作
本文介绍了一种使用 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 命令从文件的特定位置开始传输,然后在传输完所需的字节数后中断下载。