* { box-sizing: border-box; } body {margin: 0;}
在互联网上传输文件时,压缩文件有很多好处。在大多数情况下,压缩格式的所有文件的总大小会下降一个很好的幅度。这意味着你将节省一些带宽,而用户也将获得更快的下载速度。一旦用户下载了一个文件,他们可以随时解压。简而言之,压缩可以使在互联网上提供文件对你和你的访问者来说都容易得多。
有一个因素会使你不愿意压缩文件,或者使这个过程非常令人厌烦,那就是你可能要手动进行压缩。幸运的是,PHP有很多专门处理文件压缩和提取的扩展。你可以使用这些扩展中的功能,在PHP中自动压缩文件。
本教程将教你如何在PHP中压缩和解压(压缩和提取)文件,并从一个压缩包中提取文件。还将学习如何删除或重命名压缩包中的文件,而不必先提取它们。
在PHP中压缩单个文件
PHP ZipArchive
类有很多属性和方法,可以帮助你压缩和解压你的所有文件。
你可以一次添加文件到你的压缩档案中,或者一次添加整个目录。无论哪种情况,第一步是创建一个新的ZipArchive
实例,然后调用open($filename, [$flags])
方法。这个方法将打开一个新的ZIP压缩包,用于读取、写入或其他修改。可选的$flag
参数有五个有效值,决定如何处理不同的情况。
**ZipArchive::OVERWRITE**
-如果指定的归档文件已经存在,这个标志将覆盖它的内容。ZipArchive::CREATE
-如果不存在,这个标志将创建一个新的存档。ZipArchive::EXCL
-如果存档已经存在,该标志将导致错误。ZipArchive::CHECKCONS
-这个标志将告诉 PHP 对归档文件进行额外的一致性检查,如果检查失败将给出一个错误。**ZipArchive::RDONLY**
-这个标志在 PHP 7.4.3 中可用,允许以只读模式打开一个归档文件。
你可以查看这个方法的文档,了解在打开文件失败的情况下返回的不同错误代码。如果压缩文件被成功打开或创建,该方法将返回true
。
你必须在不同的情况下使用不同的标志组合来避免任何意外的结果。例如,如果你不关心现有压缩文件的内容,就使用ZipArchive::OVERWRITE
标志。如果没有以这个名字存在的档案,它就不会创建一个新的档案。
你可以使用ZipArchive::OVERWRITE
和ZipArchive::CREATE
的组合,以便覆盖现有的存档,并在没有特定名称的情况下创建一个新的存档。下面是一个例子。
$zip->open('my_compressed_files.zip', ZipArchive::OVERWRITE|ZipArchive::CREATE);
当使用ZipArchive::CREATE
标志时,你会注意到它开始修改一个已经存在的存档。这可能是你真正想做的,但它也可能产生一些意想不到的后果。你可以使用ZipArchive::CREATE
和ZipArchive::EXCL
的组合来确保你只处理以前不存在的存档。
$zip->open('new_compressed_files.zip', ZipArchive::EXCL|ZipArchive::CREATE);
一旦你成功地打开了存档,你可以使用addFile($filename, $localname, $start, $length)
方法将指定路径下的任何文件添加到存档中。$filename
参数是你要添加到存档的文件的路径。$localname
参数用于为文件分配一个名称,以便将其存储在存档中。你可以在每次要添加新的文件到存档中时调用addFile()
。
在将所有必要的文件添加到归档中后,你可以简单地调用close()
方法来关闭它并保存更改。
假设你有一个网站,允许用户下载不同字体的文件,以及使用这些字体的许可信息。像这样的文件将是使用PHP自动归档的完美例子。下面的代码告诉你如何做到这一点。
<?php
$zip = new ZipArchive();
$zip->open('compressed/font_files.zip', ZipArchive::CREATE);
$zip->addFile('fonts/Monoton/Monoton-Regular.ttf', 'Monoton-Regular.ttf');
$zip->addFile('fonts/Monoton/OFL.txt', 'license.txt');
$zip->close();
?>
我们首先创建一个ZipArchive
实例,然后使用open()
方法来创建我们的存档。addFile()
方法将我们实际的**.ttf字体文件和.txt**许可证文件添加到存档中。
你应该注意到,原来的文件是在fonts/Monoton目录下的。然而,PHP代码直接把它放在了我们存档的根目录下。你可以改变目录结构以及存档中的文件名称。
压缩一个目录中的多个文件
将单个文件添加到你的归档文件中,一段时间后会变得很累。例如,你可能想创建一个目录中所有**.pdf或.png**文件的存档。在这种情况下,addGlob($pattern, $flags, $options)
方法将被证明非常有用。这种方法的唯一缺点是,你失去了对存档中单个文件位置的控制。然而,你仍然可以使用$options
参数来影响存档内的目录结构。选项是以关联数组的形式传递的。
add_path
-分配给add_path
的值以存档中文件的本地路径为前缀。remove_path
-分配给remove_path
的值用于从添加到归档文件的不同文件的路径中移除匹配的前缀。remove_all_path
-将remove_all_path
的值设为true
,将从文件的路径中删除除名称外的所有内容。在这种情况下,文件被添加到归档文件的根部。
重要的是要记住,在移除一个路径之前,要对add_path
中指定的值进行前缀处理。
下面的代码片断将使addGlob()
和所有这些选项的使用更加清晰。
$zip = new ZipArchive();
$zip->open('compressed/user_archive.zip', ZipArchive::CREATE);
$options = array('add_path' => 'light_wallpapers/', 'remove_all_path' => TRUE);
$zip->addGlob('lights/*.jpg', 0, $options);
$options = array('add_path' => 'font_files/', 'remove_all_path' => TRUE);
$zip->addGlob('documents/*.ttf', 0, $options);
$options = array('add_path' => 'pdf_books/', 'remove_all_path' => TRUE);
$zip->addGlob('documents/*.pdf', 0, $options);
$options = array('add_path' => 'images/', 'remove_all_path' => TRUE);
$zip->addGlob('documents/*.{jpg, png}', GLOB_BRACE, $options);
$zip->close();
像往常一样,我们首先创建一个ZipArchive
实例,然后使用open()
方法来创建我们的存档。在调用addGlob()
方法之前,我们还为$options
数组中的add_path
键每次指定不同的值。这样,我们可以一次处理一组特定的文件,并提供相应的归档选项。
在第一种情况下,我们遍历lights目录下的所有**.jpg文件,并把它们放在归档的light_wallpapers目录下。同样地,我们遍历文档目录中所有的.ttf文件,然后把它们放在存档中一个叫做font_files的文件夹里。最后,我们一次遍历文档中所有的.jpg和.png文件,并将它们全部放在images**目录中。
正如你所看到的,$options
参数中的值对于组织存档中的内容很有用。
修改或覆盖存档
你可以结合不同的标志和选项来实现存档内特定的文件和目录结构。正如我前面提到的,我们可以使用open()
方法来打开一个新的或现有的存档进行读写。
在本节中,我们将学习由于传递给open()
方法的标志和传递给$options
方法的addGlob()
方法的不同组合而可能产生的差异。假设没有名为compressed_files.zip的档案,我们运行以下代码。
<?php
$zip = new ZipArchive();
$zip->open('compressed_files.zip', ZipArchive::CREATE);
$options = array('add_path' => 'wallpapers/');
$zip->addGlob('files/images/*.*', 0, $options);
$zip->close();
?>
它将从目录files/images中获取所有图像文件,并将它们放在存档根目录wallpapers中。存档中的图像文件的路径将是wallpapers/files/images。
你可以通过使用以下一组选项来摆脱所有图像的初始路径。
$options = array('add_path' => 'wallpapers/', 'remove_all_path' => TRUE);
现在图像文件将直接存储在wallpapers里面,原始图像路径将被丢弃。
正如我前面提到的,ZipArchive::CREATE
将创建一个新的存档,或者开始修改现有的存档,如果它已经存在。你不会得到任何关于现有存档被修改的警告。这意味着,如果你执行本节的代码来创建存档,然后在用新的选项替换了原来的选项后再执行,你会得到一些意外的结果。
归档文件将包含一个带有子目录files/images的壁纸目录,以及直接在壁纸目录中的图像。刚开始的时候可能会有点混乱,让你不明白为什么你设置的$options
,却没有得到尊重。
如果你想覆盖归档文件中已经存在的所有内容,但又不想让代码在不存在的情况下出错,那么一定要确保使用ZipArchive::CREATE|ZipArchive::OVERWRITE
。
还要记住,自 libzip 1.6.0 起,一个空文件不再被认为是一个有效的存档。
从一个存档中提取内容
ZipArchive
类有一个叫做extractTo($destination, $entries)
的方法来提取一个归档文件的内容。你可以用它来提取存档中的所有内容,或者只提取一些特定的文件。$entries
参数可以用来指定要提取的单个文件名,也可以用它来传递一个文件阵列。
需要记住的一点是,你需要指定存档内文件的正确路径,以便提取它。例如,我们在上一节中存档了一个名为AlegreyaSans-Light.ttf的字体文件。该文件被储存在存档中一个名为font_files的目录中。这意味着你需要在$entries
参数中指定的路径将是font_files/AlegreyaSans-Light.ttf,而不是简单的AlegreyaSans-Light.ttf。
在提取过程中,目录和文件结构将被保留,文件将被提取到它们各自的目录中。
<?php
$zip = new ZipArchive();
$zip->open('compressed/user_archive.zip', ZipArchive::CREATE);
$zip->extractTo('uncompressed/', 'font_files/AlegreyaSans-Light.ttf');
$zip->close();
?>
如果你省略第二个参数,该方法将提取档案中的所有文件。
获得对存档的更多控制
ZipArchive
类也有很多其他的方法和属性,以帮助你在提取所有内容之前获得更多关于存档的信息。
你可以使用count()
方法来计算存档中的文件数量。另一个选择是使用numFiles
属性。它们可以用来遍历存档中的所有文件,只提取你需要的文件--或者你可以对它们做一些其他的事情,比如从存档中删除它们。
在下面的例子中,我们要删除存档中所有包含斜体字的文件。类似的代码也可以用来删除所有不包含特定单词的文件。你也可以遍历这些文件,用其他东西替换某个特定的词。
<?php
$zip = new ZipArchive();
$zip->open('compressed/user_archive.zip', ZipArchive::CREATE);
$file_count = $zip->count();
for($i = 0; $i < $file_count; $i++) {
$file_name = $zip->getNameIndex($i);
if(stripos($file_name, 'Italic') !== false) {
$zip->deleteName($file_name);
}
}
$zip->close();
?>
在上面的代码中,我们使用deleteName()
来删除一个单独的文件。然而,你也可以用它来删除整个目录。
一个类似的函数renameName($oldname, $newname)
,可以用来改变存档中任何文件的名称。如果一个名为$newname
的文件已经存在,你会得到一个错误。
最后的思考
我们已经介绍了ZipArchive
类的一些非常有用的方法,这些方法将使PHP中自动压缩和提取文件变得轻而易举。现在,你应该能够根据你自己的标准,一次性地压缩单个文件或一组文件。同样,你应该能够从档案中提取任何特定的文件而不影响其他内容。
在count()
和numFiles
的帮助下,你将获得对单个文件的更多控制,重命名或删除它们将变得超级容易。你应该至少翻阅一次文档,了解更多此类功能。