在PHP中压缩文件夹的内容

3
在将此帖标记为重复之前,请注意我已经在SO上搜索答案,并且迄今为止找到的答案(以下列出)并不是我正在寻找的完全符合要求的答案。 这只是我查看过的一些问题。
我的问题是:我不能使用addFromString方法,我必须使用addFile方法,这是任务的要求。
我已经尝试了几种方法,以下是我的当前版本:
public function getZippedFiles($path)
{
    $real_path = WEBROOT_PATH.$path;

    $files = new RecursiveIteratorIterator (new RecursiveDirectoryIterator($real_path), RecursiveIteratorIterator::LEAVES_ONLY);

    //# create a temp file & open it
    $tmp_file = tempnam($real_path,'');
    $zip_file = preg_replace('"\.tmp$"', '.zip', $tmp_file);

    $zip = new ZipArchive();
    $zip->open($zip_file, ZipArchive::CREATE);

    foreach ($files as $name=>$file)
    {
        error_log(print_r($name, true));
        error_log(print_r($file, true));
        if ( ($file == ".") || ($file == "..") )
        {
            continue;
        }

        $file_path = $file->getRealPath();
        $zip->addFile($file_path);
    }

    $zip->close();
}

当我尝试打开生成的文件时,会提示“Windows无法打开文件夹。压缩(zipped)文件夹''无效。”

我已成功使用addFromString完成了任务,如下所示:

$file_path = WEBROOT_PATH.$path;
    $files = array();
    if (is_dir($file_path) == true)
    {
        if ($handle = opendir($file_path))
        {
            while (($file = readdir($handle)) !== false)
            {
                if (is_dir($file_path.$file) == false)
                {
                    $files[] = $file_path."\\".$file;
                }
            }

            //# create new zip opbject
            $zip = new ZipArchive();

            //# create a temp file & open it
            $tmp_file = tempnam($file_path,'');
            $zip_file = preg_replace('"\.tmp$"', '.zip', $tmp_file);

            $zip->open($zip_file, ZipArchive::CREATE);

            //# loop through each file
            foreach($files as $file){

                //# download file
                $download_file = file_get_contents($file);

                //#add it to the zip
                $zip->addFromString(basename($file),$download_file);

            }

            //# close zip
            $zip->close();
        }
    }
}

上面的内容大部分都是从某个地方看到的示例代码直接复制过来的。如果有人能指点我一个好的方向,我将不胜感激!
***** 更新 ***** 我加了一个if语句来围绕着关闭标签,就像这样:
if (!$zip->close()) {
    echo "failed writing zip to archive";
}

该消息被回显出来,显然问题就在那里。我还检查了$zip->open()是否正常工作,并确认它可以正常打开。

2个回答

0

终于成功使用addFile让某些东西工作了。

我创建了一个包含3个函数的帮助类:一个用于列出目录中的所有文件,一个用于压缩所有这些文件,以及一个用于下载已压缩的文件:

<?php
require_once($_SERVER["DOCUMENT_ROOT"]."/config.php");

class FileHelper extends ZipArchive
{
    /**
     * Lists files in dir
     * 
     * This function expects an absolute path to a folder intended as the target.
     * The function will attempt to create an array containing the full paths to 
     * all files in the target directory, except for . and ..
     * 
     * @param dir [string]    : absolute path to the target directory
     * 
     * @return result [array] : array of absolute paths pointing to files in the target directory
     */
    public static function listDirectory($dir)
    {
        $result = array();
        $root = scandir($dir);
        foreach($root as $value) {
            if($value === '.' || $value === '..') {
                continue;
            }
            if(is_file("$dir$value")) {
                $result[] = "$dir$value";
                continue;
            }
            if(is_dir("$dir$value")) {
                $result[] = "$dir$value/";
            }
            foreach(self::listDirectory("$dir$value/") as $value)
            {
                $result[] = $value;
            }
        }
        return $result;
    }

    /**
     * Zips and downloads files
     * 
     * This function expects a directory location as target for a temp file, and a list(array)
     * of absolute file names that will be compressed and added to a zip file. After compression,
     * the temporary zipped file will be downloaded and deleted.
     * 
     * @param location [string] : absolute path to the directory that will contain the temp file
     * @param file_list [array] : array of absolute paths pointing to files that need to be compressed
     * 
     * @return void
     */
    public function downloadZip($file)
    {
        $modules = apache_get_modules();
        if (in_array('mod_xsendfile', $modules)) // Note, it is not possible to detect if X-SendFile is turned on or not, we can only check if the module is installed. If X-SendFile is installed but turned off, file downloads will not work
        {
            header("Content-Type: application/octet-stream");
            header('Content-Disposition: attachment; filename="'.basename($file).'"');
            header("X-Sendfile: ".realpath(dirname(__FILE__)).$file);

            // Apache will take care of the rest, so terminate the script
            exit;
        }

        header("Content-Type: application/octet-stream");
        header("Content-Length: " .(string)(filesize($file)) );
        header('Content-Disposition: attachment; filename="'.basename($file).'"');
        header("Content-Transfer-Encoding: binary");
        header("Expires: 0");
        header("Cache-Control: no-cache, must-revalidate");
        header("Cache-Control: private");
        header("Pragma: public");

        ob_end_clean(); // Without this, the file will be read into the output buffer which destroys memory on large files
        readfile($file);
    }

    /**
     * Zips files
     * 
     * This function expects a directory location as target for a temp file, and a list(array)
     * of absolute file names that will be compressed and added to a zip file. 
     * 
     * @param location [string]  : absolute path to the directory that will contain the temp file
     * @param file_list [array]  : array of absolute paths pointing to files that need to be compressed
     * 
     * @return zip_file [string] : absolute file path of the freshly zipped file
     */
    public function zipFile($location, $file_list)
    {
        $tmp_file = tempnam($location,'');
        $zip_file = preg_replace('"\.tmp$"', '.zip', $tmp_file);

        $zip = new ZipArchive();
        if ($zip->open($zip_file, ZIPARCHIVE::CREATE) === true)
        {
            foreach ($file_list as $file)
            {
                if ($file !== $zip_file)
                {
                    $zip->addFile($file, substr($file, strlen($location)));
                }
            }
            $zip->close();
        }

        // delete the temporary files
        unlink($tmp_file);

        return $zip_file;
    }
}
?>

这是我如何调用这个类的函数:

$location = "d:/some/path/to/file/";
$file_list = $file_helper::listDirectory($location);
$zip_file = $file_helper->zipFile($location, $file_list);
$file_helper->downloadZip($zip_file);

0

看看这个解决方案,更清晰明了:

function Zip($source, $destination)
{
    if (!extension_loaded('zip') || !file_exists($source)) {
        return false;
    }

    $zip = new ZipArchive();
    if (!$zip->open($destination, ZIPARCHIVE::CREATE)) {
        return false;
    }

    $source = str_replace('\\', '/', realpath($source));

    if (is_dir($source) === true)
    {
        $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST);

        foreach ($files as $file)
        {
            $file = str_replace('\\', '/', $file);

            // Ignore "." and ".." folders
            if( in_array(substr($file, strrpos($file, '/')+1), array('.', '..')) )
                continue;

            $file = realpath($file);

            if (is_dir($file) === true)
            {
                $zip->addEmptyDir(str_replace($source . '/', '', $file . '/'));
            }
            else if (is_file($file) === true)
            {
                $zip->addFile($file, str_replace($source . '/', '', $file));
            }
        }
    }
    else if (is_file($source) === true)
    {
        $zip->addFile($file, str_replace($source . '/', '', $file));
    }

    return $zip->close();
}

并像这样使用:

Zip('/folder/to/compress/', './compressed.zip');

使用addFromString检查原始帖子


我相信你的回答通常可能是一个好的解决方案,但我不能使用它,因为“我的问题是:我不能使用addFromString,我必须使用addFile,这是任务的要求。”我不能使用addFromString,我必须使用addFile,但我一直无法让它工作。唯一的错误提示是Windows无法打开压缩文件。 - Skytiger
1
@Skytiger 很抱歉没有仔细阅读要求。您能告诉我为什么这是一个要求吗?性能?客户、老板、学术?我更新了问题,并使用addFile版本完成了完全相同的工作。 - Guilherme Viebig
这是老板偏好和性能提升的混合体。我成功地得到了一个可行的解决方案,你可以在我的回答中看到 :) - Skytiger
1
@Skytiger 是的,我试过了。很奇怪,但在我的测试中,使用这段代码压缩phpmyadmin文件夹内容时,addFromString更快。然后我尝试了一个有很多文件和目录的300mb文件夹..再次使用addFromString更快。也许file_get_contents比zip addFile内部更优化。 - Guilherme Viebig
当这段代码开始运行时,我们将面临的问题是需要压缩的文件的平均文件大小约为20GB。据我所知,当文件大小达到那么大时,addFromString方法开始落后于addFile方法。 - Skytiger

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