使用PHP创建.tar.gz文件

6
我正在处理的项目需要创建.tar.gz归档文件并将其传输到外部服务中。这个外部服务只接受.tar.gz格式的文件,因此其他类型的文件不行。执行代码的服务器不允许访问系统调用,因此system、exec、backticks等都不行。这意味着我必须依靠纯PHP实现来创建.tar.gz文件。
经过一些研究,似乎PharData对于实现这个目标很有帮助。然而,我在使用它时遇到了困难,需要一些指导。
考虑以下文件夹布局:
parent folder
  - child folder 1
  - child folder 2
  - file1
  - file2

我使用以下代码片段创建.tar.gz归档文件,它能够实现所需功能,但最终结果有一个小问题,即它不包含父文件夹,只包含其中的所有内容。
$pd = new PharData('archive.tar');

$dir = realpath("parent-folder");

$pd->buildFromDirectory($dir);

$pd->compress(Phar::GZ);

unset( $pd );

unlink('archive.tar');

创建归档文件时,必须包含上述精确的文件夹布局。使用上述代码片段,归档文件包含除父文件夹之外的所有内容,这对于外部服务来说是无法接受的:

- child folder 1
- child folder 2
- file1
- file2

从目录中的文件构建tar/zip归档。

虽然buildFromDirectory的描述中并没有包含父文件夹在归档中是可以理解的,但我也尝试过使用buildFromIterator,但最终结果也是一样的,即归档中不包含父文件夹。我能够使用addFile获得所需的结果,但这个方法非常缓慢。

经过进一步研究,我找到了以下库:https://github.com/alchemy-fr/Zippy。但这需要composer支持,而服务器上没有此功能。如果有人能指导我实现最终结果将不胜感激。我也愿意使用其他方法或库,只要它是纯PHP实现且不需要任何外部依赖项。不确定是否有帮助,但执行代码的服务器具有PHP 5.6。


你不需要在服务器上安装Composer,只需要在本地开发机器上安装Composer即可。 - odan
@DanielO,非常感谢您提供的精彩信息,我很感激。正如您已经了解的那样,我的composer经验有限!:) 我会尝试一下并看看结果。 - Anand Shah
2个回答

4

使用“parent-folder”的父级作为Phar::buildFromDirectory()的基础,并使用第二个参数将结果限制为仅包括“parent-folder”,例如:

$parent = dirname("parent-folder");
$pd->buildFromDirectory($parent, '#^'.preg_quote("$parent/parent-folder/", "#").'#');
$pd->compress(Phar::GZ);

Paul,感谢你提供的代码片段,非常好用。使用buildFromIterator能否实现同样的功能呢?就像一个英雄一样,我去向我的PM展示代码,他说:“哦,但我们需要排除某些文件夹”,唉! - Anand Shah
实际上,我使用了 CallbackFilterIterator,它解决了问题。 - Anand Shah

3

我最终不得不这样做,因为这个问题在谷歌上是搜索结果中的第一位。这是一种最佳的方法,而且不需要使用正则表达式(如果你想从包含许多其他目录的目录中提取一个目录,则正则表达式无法很好地扩展)。

function buildFiles($folder, $dir, $retarr = []) {
    $i = new DirectoryIterator("$folder/$dir");
    foreach ($i as $d) {
        if ($d->isDot()) {
            continue;
        }
        if ($d->isDir()) {
            $newdir = "$dir/" . basename($d->getPathname());
            $retarr = buildFiles($folder, $newdir, $retarr);
        } else {
            $dest = "$dir/" . $d->getFilename();
            $retarr[$dest] = $d->getPathname();
        }
    }
    return $retarr;
}
 
$out = "/tmp/file.tar";
$sourcedir = "/data/folder";
$subfolder = "folder2";
$p = new PharData($out);
$filemap = buildFiles($sourcedir, $subfolder);
$iterator = new ArrayIterator($filemap);
$p->buildFromIterator($iterator);
$p->compress(\Phar::GZ);
unlink($out); // $out.gz has been created, remove the original .tar

这使你能够从 /data/folder 中选择 /data/folder/folder2,即使 /data/folder 包含数百万个其他文件夹。然后,它将创建一个 tar.gz 文件,其中所有内容都以文件夹名称为前缀。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接