<chsdate w:st="on" year="2007" month="6" day="08" islunardate="False" isrocdate="False"><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Arial; mso-font-kerning: 0pt">2007 </span><span style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体; mso-font-kerning: 0pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial">年</span><span lang="EN-US" style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: Arial; mso-font-kerning: 0pt"> 6 </span><span style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: 宋体; mso-font-kerning: 0pt; mso-ascii-font-family: Arial; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial">月</span></chsdate> 08 日
需要使用 ZIP?现在可用了!PHP 最近把 ZIP 功能添加到了 PHP V5.2 中。这些功能现已被内置于 PHP V5.2 中。本文是“What's new in PHP V5.2”系列文章(共五部分)的第 4 部分,将向您展示如何通过创建、编辑、上传和读取以及创建和下载 ZIP 文件来充分利用此新增功能。阅读本文后,您将十分精通使用 PHP V5.2 中的最新 ZIP PHP 扩展处理 ZIP 文件的技巧,这样在用 PHP 处理大型文件时就可以降低带宽负载和存储设备使用量。
ZIP 简介
1989 年引入的 ZIP 文件格式通过删除文件中的空白空间和冗余信息以及把多个文件打成一个包的方式,帮助计算机用户传输大型文件。ZIP 最初是由 Phil Katz 为共享件程序 PKZIP 创建的,ZIP 是一种快速、可靠且遵循开放规范的压缩格式。这些因素使 ZIP 在 PC 用户中大获成功,并且其开放规范使程序员可以把 ZIP 构建到 Windows®、OS X、Konqueror 和 Nautilus 文件管理器中。考虑使用几乎所有计算机都可以处理的 ZIP 文件压缩格式是十分安全的。
压缩通常有很多好处,可以缩小文件大小,也可以把若干个文件绑定为一个归档文件。通过减少文件大小,带宽和存储设备负载也会随之减少。文件大小要求很明显,但是当惟一的其他选择是一个接一个地下载时,能够把若干个文件绑定在一起就是一个巨大的优点。您可以为用户提供把许多单个文件创建为一次下载的功能,使您可以更便捷地把产品或服务交付给用户。
由于几乎所有计算机中都可以使用 ZIP,因此使用它来压缩应用程序是非常好的选择,尤其是在用户将把文件下载到桌面型计算机中时。例如,Windows XP 用户可以便捷地使用本机 ZIP 支持,称为压缩文件夹。当 Windows XP 用户下载 ZIP 文件时,它显示为用户可以双击打开的压缩文件夹。OS X、Konqueror 和 Nautilus 用户也只需双击 ZIP 文件即可打开文件。
被压缩掉的空间去了哪里?
被选中放入归档(ZIP 文件的另一个名称)中的文件都列在文件结构中,然后就被缩小或压缩了。文件都是使用众多压缩算法中的一种算法压缩成一个 ZIP 文件的,但这些压缩算法都是以相同的方法操作:删除冗余数据或空白。这种方法在应用到可以减少大量重复文字和标点符号来节省空间的文本文件中时尤为有效。当处理图形文件(例如 JPEG 图像)时,压缩就不那么好用了,因为数据更加难于减少重复。
减少文件大小有若干种方法,并且对于 ZIP 格式,它们全都是常见方法。可以运用 ZIP 的每种语言或每个应用程序都知道这些格式,而且 PHP 也不例外。PHP 将像其他应用程序一样使用这些算法之一来压缩文件,并获得任何其他语言或程序将获得的结果。让我们来看一个示例:
让我们引用我最崇拜的历史人物之一邱吉尔经常说的一段话。文本文件 Winston.txt 包含以下文本:“You make a living by what you get; you make a life by what you give.”。
要压缩文件 Winston.txt,可以删除不必要的空格,方法为创建索引 —— 就像搜索引擎那样操作 —— 并用此索引的副本和索引组件布局的方法清单来替换文件组件。使用像本例这样非常小的示例,如果没有重复的单词,则索引的大小会十分接近示例的大小。仅当单词重复出现时,才能将其添加到索引中,因此这应当不会成为一个问题。
在我们的示例中,有以下重复的单词:
1. “we” 使用了四次
2. “make” 使用了两次
3. “a” 使用了两次
4. “by” 使用了两次
5. “what” 使用了两次
还剩下几个只需写出的单个单词:
1. “living”
2. “get”
3. “life”
4. “give”
如果使用索引写出我们的示例,则得到 “1 2 3 living 4 5 1 get; 1 2 3 life 4 5 1 give.”。
您已经可以看到该文件现在包含的信息要少一些。为了准确,您可以算一下并确定省去了多少字符。原文件共有 64 个字符(包括标点符号)。如果把索引中的注释减少为更简单的内容(如 1we4),则将把索引减少到 23 个字符并加上实际字符串字符总数 33 等于 56。文件大小没有大幅减少,但是如果把演说的其余部分添加到文件中,您将会开始看到压缩中的重大改进。索引将变得更大,但是每个单词的实例数也将增加。索引负载将开始减少,并且您将看到一个优秀的压缩率。
文本文件的压缩率平均为 70% 左右;而对于更复杂更难于压缩的文件(例如图像),其压缩率大约 10%。
ZIP 中有哪些内容?
由于 ZIP 归档可以有多个文件,因此 ZIP 归档都有自己的文件结构,就像您的本地计算机的文件系统一样。此结构是围绕条目构建的,并允许巧妙的编码器在解压缩时从许多文件中只选出一个文件。当您只需要归档文件中的一张图片或一个文本文件时,此结构会十分便捷。此结构还将维护目录信息,目录信息在传输 Web 站点或有文件系统关系依赖性的其他文件组时将极为有用。
在 PHP 脚本中,每个文件都有一组可用的文件相关信息。打开文件时,您将能够在创建的 ZipArchive 对象中访问这些信息。这些信息可用于各种目的,例如检验解压缩后的文件大小或只创建 ZIP 文件的内容清单而不打开它。
PHP ZIP 的部分功能允许以十分有用的方法处理这些文件结构。在下一个示例中,我们将展示几种方法查看这些功能并了解如何在创建 ZIP 文件的过程中创建 ZIP 文件结构。
继续压缩
让我们再来看一个使用 PHP 创建 ZIP 文件的示例。我将使用的代码示例几乎与 PHP.net 手册文档提供的示例完全相同,只有几处修改(有关 ZIP 功能页面,请参阅 参考资料)。让我们浏览一下创建 ZIP 文件的步骤并查看如何正确构建文件结构。我们将把字符串作为文件插入到新 ZIP 中,并把现有文件获取到新 ZIP 中。
首先,需要准备好一个要添加到 ZIP 中的文本文件。
清单 1. testtext.txt
Had I the heavens' embroidered cloths,
Enwrought with golden and silver light,
The blue and the dim and the dark cloths
Of night and light and the half-light,
I would spread the cloths under your feet:
But I, being poor, have only my dreams;
I have spread my dreams under your feet,
Tread softly because you tread on my dreams.
William Butler Yeats
|
您可以测试这段代码以直接查看这次尝试的结果。如果要测试,应当剪切和粘贴清单 1,并将它与 PHP 脚本放在同一目录下。将此文件另存为 testtext.txt,因为将在 PHP 代码中引用此名称。
接下来,需要创建 ZIP 文件。
清单 2. zipcreate.php
<?php
$zip = new ZipArchive();
$filename = "newzip.zip";
if ($zip->open($filename, ZIPARCHIVE::CREATE)!==TRUE) {
exit("cannot open <$filename>\n");
}
$zip->addFromString("firstfile." . time() . ".txt", \
"This is the first file in our ZIP, added as
firstfile.txt.\n");
$zip->addFromString("testdir/secondfile." . time() . ".txt", \
"This is the second file in our ZIP,
added as secondfile.txt in the testdir directory.\n");
echo "numfiles: " . $zip->numFiles . "\n";
$zip->close();
?>
|
首先在第一行中创建新的 ZIP 归档对象。创建了对象后,将为文件名设置一个变量,以供稍后在脚本中使用。可以每次都显式调用该文件名,但是设置一个变量以供多次使用会更轻松。常量 ZIPARCHIVE::CREATE 表示如果具有该名称的文件尚不存在,则应当创建一个新文件。如果这是您第一次运行脚本,将新建 ZIP 文件。如果这个结果没有出现,脚本将显示错误并退出。
接下来,使用 addFromString() 方法,该方法允许您使用字符串数据或通过字符串变量在 ZIP 归档中创建条目。需要创建文件句柄,才能在 ZIP 文件结构中识别文件。在这种情况下,使用了 firstfile.txt 作为文件名,同时还使用了它的创建时间。字符串数据随后被存储。接下来在处理 secondfile.txt 时将再执行一次该操作,但是这次我们添加了目录。您可以看到文件标识符 secondfile.txt 实际上列有路径的下一级。它将显示在 testdir 目录中。
我们向用户展示归档中的文件数并关闭文件,将其保存为 ZIP 归档,准备供用户下载。您可能会注意到在连续按 Refresh 或用其他方法运行 PHP 脚本时,ZIP 中的文件数会增长。这是因为一直在打开同一个目录中的同一个文件,添加两个字符串作为文件,并再次关闭。
图 1. zipcreate.php 的样例输出
<shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="_x0000_i1025" style="WIDTH: 209.25pt; HEIGHT: 209.25pt" alt="zipcreate.php 的样例输出" type="#_x0000_t75"><imagedata o:href="http://www.ibm.com/developerworks/cn/opensource/os-php-v524/fig01.jpg" src="file:///C:%5CDOCUME~1%5Csgcs1642%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image001.jpg"></imagedata></shape>
正如您会注意到的,实际上并未把新文件添加到 ZIP 中。我们需要把诗歌放在其中,以便用户获得一些训练。让我们现在就来添加。
清单 3. 将外部文件添加到 newzip.zip 中
<?php
$zip = new ZipArchive();
$filename = "newzip.zip";
if ($zip->open($filename, ZIPARCHIVE::CREATE)!==TRUE) {
exit("cannot open <$filename>\n");
}
$zip->addFromString("firstfile." . time() . ".txt", \
"This is the first file in our ZIP, added as
firstfile.txt.\n");
$zip->addFromString("testdir/secondfile." . time() . ".txt", \
"This is the second file in our ZIP,
added as secondfile.txt.\n");
$zip->addFile("testtext.txt");
echo "numfiles: " . $zip->numFiles . "\n";
$zip->close();
?>
|
这样看来,路径是相对的,只是指向了需要添加到 ZIP 中的文件并使用 addFile() 方法把它打包在其中。如果文件 testtext.txt 位于同一个目录中,这样做可以将其添加到归档中。现在已经通过任意的字符串数据创建了新归档文件并已经把外部文件添加到了归档中。这些都是创建新归档时最常见的任务。
打开归档
如果我们不能从归档中恢复出可正常使用的文件,那么 ZIP 文件就没有多大用处。一些程序可以把文件直接从归档中读出,但是这些程序必须先把文件解压缩。最常见的处理方法是,简单地打开整个文件并将其展开为单个组件文件,把这些文件准备好以供正常使用。出于我们的目的,将打开先前创建的 ZIP 文件并查看其内容。
清单 4. zipread.php
<?php
$zip = new ZipArchive();
$filename = 'newzip.zip';
if ($zip->open($filename)!==TRUE) {
exit("cannot open <$filename>\n");
}
print_r($zip);
var_dump($zip);
echo "<br><br>";
echo "The file " .$filename. " has the following files:\n <br>";
for ($i=0; $i<$zip->numFiles;$i++) {
echo "index: $i\n";
print_r($zip->statIndex($i));
echo "<br>\n";
}
$zip->extractTo('./testdestination/');
$zip->close();
?>
|
像以前一样,在名为 $zip 的变量表单中创建 ZipArchive 类的新实例。使用 ZipArchive 的 open() 方法,打开已创建的 ZIP 归档。if 语句将用作简单的错误控制,如果由于用户的小失误而导致它未找到文件,则退出脚本。如果成功打开文件,脚本将继续执行并把关于 ZIP 归档的一些信息打印给用户。
到此为止,我们完成了两项重要任务。逐个列出了 ZIP 归档中的文件。由于将从 $zip 对象输出索引数组,因此将获得大量数据,包括文件大小、校验和信息。要减少数据量,在特定索引中查看文件的各个属性即可。
一旦打印出文件中的内容,所有内容就会被释放到名为 testdestination 的目录中。如果此目录不可用,系统将为我们创建该目录。此时需要注意的是如果目录已存在,或者如果解压缩的目标目录中已经有同名文件,则 ZIP 函数将覆盖原有内容。
我们已经打开了 ZIP 归档,通过保存到本地目录为使用文件做好了准备,并且列出了内容,可为修改初始的 ZIP 文件做好准备。这些简单的任务只是一个开始,并且几乎不能用于最复杂应用程序的文件压缩。巧妙地使用压缩可以为所有类型的文件传输带来更多便利。PHP 中的 ZIP 本机支持将使大量文件传输问题得以解决。
结束语
在处理拥有大量空白或重复数据的大型文件时,ZIP 是一种十分优秀方法,能够降低带宽负载或存储设备使用量。我们可以清理掉文件中的大量冗余或空白,并使其减少到只包含基本要素,从而使文件更加紧凑。这样做都可以明显地减少文件在文件系统中占用的空间大小以及移动文件时所需的全部带宽负载。
一种可能的应用是在需要把大量文件上传到服务器上时,比如需要把照片上传到照片库中或上传许多文本文件。您可以简单地 ZIP 文件并同样让上传脚本将文件解压缩,而不用费力地在上传对话框中逐个上传文件。这可以免去必须单击 Browse 对话框的痛苦。
一般而言,ZIP 那些不会被直接访问的文件或者下载后再使用的文件是个好主意 —— 并且我们最终使 PHP 拥有了这种功能。
参考资料
学习
阅读本系列的 第 1 部分、第 2 部分 和 第 3 部分。 阅读 PHP 5.2.0 发行说明。 从 PHP.net 获得 ZIP 文件函数 的参考资料。 可在 W3School.com 获得 PHP ZIP 文件函数 的其他参考资料。 <span style= |