我的服务器每天都会分割日志,这些按天分割的日志为了方便管理和下载就需要按月归档,打包为压缩包。这里就简单写一下 PHP 压缩和解压文件。

我用来压缩和解压文件的库是 ZipArchive ,这是 PHP 内置的一个用来压缩和解压 ZIP 文件的库。

压缩

下面简单创建一个 ZIP 的压缩包,然后添加一个文件到压缩包:

$zip = new ZipArchive();  // 创建 ZipArchive 类
$zip->open('1.zip', ZipArchive::CREATE);  // 打开一个 zip 文件
$zip->addFile('log.txt');  // 添加一个文件到打开的 zip 文件中
$zip->close();  // 关闭打开的 zip 文件

上面创建了一个名为 1.zip 的压缩包,然后把 log.txt 添加到了 1.zip 中。

下面是用到的方法说明:

open() 方法

open 方法的功能是打开一个 zip 文件,第一个参数是文件名,第二个参数是打开方式,打开方式可以接受一个预定义常量,下面是预定义常量的说明:

  • ZIPARCHIVE::OVERWRITE : 总是以一个新的压缩包开始,此模式下如果已经存在则会被覆盖。
  • ZIPARCHIVE::CREATE: 如果不存在则创建一个zip压缩包。
  • ZipArchive::RDONLY: 只读模式打开压缩包。
  • ZIPARCHIVE::EXCL: 如果压缩包已经存在,则出错。
  • ZIPARCHIVE::CHECKCONS: 对压缩包执行额外的一致性检查,如果失败则显示错误。

我上面使用的是 ZipArchive::CREATE,也就是如果 zip 文件不存在就创建一个。

addFile() 方法

addFile 方法的功能是把文件添加到 zip 包中。第一个参数是要添加的文件名,第二个参数是添加到 zip 包中的名称。

下面演示第二个参数:

$zip->addFile('log.txt', 'log-backup.txt');

我要添加到 zip 包的文件是 log.txt 添加到 zip 包后在 zip 包中查看就是 log-backup.txt,相当于在添加的时候给文件改个名,成功会返回 true,否则返回 false 。

close() 方法

在操作完成后可以使用 close 关闭打开的 zip 包。

直接添加内容

在没有文件的情况下也可以直接添加字符串或其它内容到 zip 包中。

下面创建一个 zip 包,然后添加字符串到 zip 包中:

$zip = new ZipArchive();  // 创建 ZipArchive 类
$zip->open('1.zip', ZipArchive::CREATE);  // 打开一个 zip 文件
$zip->addFromString('Hello.txt', 'Hello this is text');
$zip->close();  // 关闭打开的 zip 文件

上面创建了一个 1.zip 的压缩包,然后添加了一个名为 Hello.txt 的文件到 zip 包中,这个 Hello.txt 的内容就是 Hello this is text 。

添加多个文件

添加多个文件除了多次调用添加外也可以使用 addGlob 方法添加,addGlob 方法就和 glob 函数差不多,可以根据给定的过滤方式来获取目录下所有符合标准的文件。

下面创建一个 zip 包,然后把当前目录下所有 txt 结尾的文件都添加到 zip 包中:

$zip = new ZipArchive();  // 创建 ZipArchive 类
$zip->open('1.zip', ZipArchive::CREATE);  // 打开一个 zip 文件
$zip->addGlob('*.txt');
$zip->close();  // 关闭打开的 zip 文件

如果我要添加当前目录下的所有文件可以这样写:

$zip->addGlob('*');

addGlob 方法只能添加文件,即便用 * 也不会添加目录。

添加目录

下面创建一个 zip 包,然后添加一个目录到 zip 包中:

$zip = new ZipArchive();  // 创建 ZipArchive 类
$zip->open('1.zip', ZipArchive::CREATE);  // 打开一个 zip 文件
$zip->addEmptyDir('.idea');
$zip->close();  // 关闭打开的 zip 文件

上面的 addEmptyDir 方法只能添加目录,目录中的文件或目录是不会被添加到 zip 包中的,也就是说添加到 zip 包中的是空目录。

因为 ZipArchive 没有可以自动递归添加目录下的文件或目录的方法,所以如果要添加目录下的文件或目录就只能自己手动递归添加。

下面简单编写一个可以递归添加目录下的所有文件或目录的类:

class Zip {
    protected $zip = null;  // ZipArchive 对象
    protected $count = 0;  // 记录文件数量
    // 添加单个文件或目录
    public function add($fileName, $zipFileName = '') {
        if ($zipFileName == '') {
            // 如果 zip 文件名为空就使用传入的文件或目录名作为 zip 文件名
            $zipFileName = $fileName . '.zip';
        }
        $this->zip = new ZipArchive();  // 创建 ZipArchive 类
        $this->zip->open($zipFileName, ZipArchive::CREATE);  // 打开一个 zip 文件
        // 判断传入的是否是一个目录
        if (is_dir($fileName)) {
            $this->addDir($fileName);  // 调用添加目录方法
        }else {
            // 如果不是目录就直接添加文件
            if ($this->zip->addFile($fileName)) {
                $this->count ++;  // 成功添加后添加文件数量 +1
            }
        }
        $this->zip->close();  // 关闭打开的 zip 包
        return $this->count;  // 返回已添加文件的数量
    }
    // 添加目录
    public function addDir($dir) {
        $this->zip->addEmptyDir($dir);  // 添加目录
        $fileList = glob($dir .  '/*');  // 获取路劲下的所有文件或目录
        foreach ($fileList as $file) {
            // 是否是目录
            if (is_dir($file)) {
                $this->addDir($file);  // 调用自身继续操作
                continue;  // 跳过本次循环
            }
            // 添加文件
            if ($this->zip->addFile($file, $file)) {
                $this->count ++;  // 成功添加后添加文件数量 +1
            }
        }
    }
}

上面的类可以调用 add 方法来添加文件或目录,第一个参数可以传入要添加的文件或目录,第二个参数是 zip 包名称,如果传入的 zip 包不存在就会自动创建,如果省略第二个参数会使用文件名来作为 zip 包名。

下面调用一下上面的 zip 类:

$zip = new Zip();
$zip->add('src', 'src.zip');  // 调用 add 添加

使用上面的类无论目录下有多少文件或目录都能添加。

解压文件

下面把 src.zip 里的所有文件解压到 dir 目录:

$zip = new ZipArchive();  // 创建 ZipArchive 对象
$zip->open('src.zip');  // 打开 zip 包
$zip->extractTo('dir');  // 把 zip 包内的所有文件解压到指定目录
$zip->close();  // 关闭打开的 zip 包

解压文件使用的就是 extractTo 方法,参数就是存放解压后的文件的目录,如果传入的目录不存在会自动创建。extractTo 成功返回 true ,失败返回 false 。

设置和获取注释

使用 WinRAR 之类的软件查看一些网上下载的压缩包的时候可以看到右侧有注释内容,PHP 也能设置和查看注释内容。

下面给 src.zip 设置注释内容:

$zip = new ZipArchive();  // 创建 ZipArchive 对象
$zip->open('src.zip');  // 打开 zip 包
$zip->setArchiveComment('这是一个 zip 压缩包');  // 设置注释
$zip->close();  // 关闭打开的 zip 包

setArchiveComment 方法可以给打开的压缩包设置注释内容,参数就是注释内容,成功返回 true,失败返回 false 。

下面输出 src.zip 的注释内容:

$zip = new ZipArchive();  // 创建 ZipArchive 对象
$zip->open('src.zip');  // 打开 zip 包
echo $zip->getArchiveComment();  // 输出注释内容
$zip->close();  // 关闭打开的 zip 包

查询相关

下面是一些获取 zip 压缩包信息和文件的方法:

count() 方法

count 方法可以获取压缩包中的文件和目录数量,获取的是所有文件和目录的数量,包括深层级的目录和文件。

getNameIndex() 方法

getNameIndex 方法可以根据传入的索引返回压缩包内文件的文件名,需要传入一个数字索引,返回对应文件的文件名。

下面循环输出 src.zip 里所有文件的文件名:

$zip = new ZipArchive();  // 创建 ZipArchive 对象
$zip->open('src-all.zip');  // 打开 zip 包
for ($i = 0;$i < $zip->count();$i ++) {
    echo $zip->getNameIndex($i) . "/n";  // 输出文件名
}
$zip->close();  // 关闭打开的 zip 包

getFromIndex() 方法

getFromIndex 方法可以根据索引读取压缩包内的文件内容,第一个参数是文件索引,第二个参数是读取的字节数,0 为读取整个文件。

下面读取并输出 src.zip 中的第 2 个文件的内容,只读取 3 个字节:

$zip = new ZipArchive();  // 创建 ZipArchive 对象
$zip->open('src.zip');  // 打开 zip 包
echo $zip->getFromIndex(2, 3);  // 读取并输出
$zip->close();  // 关闭打开的 zip 包

getFromName() 方法

getFromName 方法可以根据压缩包内的文件名读取压缩包内的文件内容,第一个参数是要读取的文件名,第二个参数是读取的字节数,0 为读取整个文件。getFromName 和上面的 getFromIndex 方法都是差不多的,只是 getFromName 方法传入的是文件名。

操作压缩包内的文件

下面是一些操作压缩包内的文件的方法:

renameName() 方法

renameName 方法可以给压缩包内的文件重命名,第一个参数是要重命名的文件名,第二个参数是新文件名,成功返回 true ,失败返回 false 。

下面把 src.zip 里的 log.txt 重命名为 new-name.txt :

$zip = new ZipArchive();  // 创建 ZipArchive 对象
$zip->open('src.zip');  // 打开 zip 包
$zip->renameName('log.txt', 'new-name.txt');  // 重命名
$zip->close();  // 关闭打开的 zip 包

renameIndex() 方法

renameIndex 方法和上面的 renameName 方法差不多,也是给压缩包里的文件重命名的,不过 renameIndex 的第一个参数是要重命名的文件索引,第二个参数是新文件名,成功返回 true ,失败返回 false 。

deleteName() 方法

deleteName 方法可以删除压缩包内的文件, 参数就是要删除的文件名,成功返回 true ,失败返回 false 。

下面删除 src.zip 中的 log.txt :

$zip = new ZipArchive();  // 创建 ZipArchive 对象
$zip->open('src.zip');  // 打开 zip 包
$zip->deleteName('log.txt');  // 删除
$zip->close();  // 关闭打开的 zip 包

deleteIndex() 方法

deleteIndex 方法和上面的 deleteName 差不多,也是删除压缩包里的文件,不过 deleteIndex 需要传入文件索引。

以上就是 PHP 操作 zip 压缩文件的方法,更多方法可以查看 PHP 官网的 ZipArchive 类说明。