duanchen7036 2015-03-17 15:05
浏览 60
已采纳

在PHP中压缩文件夹的内容

Before marking this post as a duplicate, please note that I have already searched for answer on SO and the once I've found so far (listed below) haven't been exactly what I've been looking for.

Those are just some of the ones I've looked at.

My problem is this: I can't use addFromString, I have to use addFile, it's a requirement of the task.

I've already tried a couple of ways, here's my current iteration:

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();
}

When I try to open the resulting file, I get told that "Windows cannot open the folder. The Compressed(zipped) Folder '' is invalid."

I've managed to succesfully complete the task using addFromString, like so:

$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();
        }
    }
}

The above is mostly just copied straight from some example code I saw somewhere. If anyone can point me in a good direction I'd be very grateful!

***** UPDATE ***** I added an if around the close like this:

if (!$zip->close()) {
    echo "failed writing zip to archive";
}

The message gets echoed out, so obviously the problem is there. I've also checked to make sure the $zip->open() works, and I've confirmed that it is opening it without a problem.

  • 写回答

2条回答 默认 最新

  • doulandai0641 2015-03-18 09:56
    关注

    Finally managed to get something working using addFile.

    I created a helper class that contains 3 functions: one to list all the files in a directory, 1 to zip all those files, and 1 to download the zipped files:

    <?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;
        }
    }
    ?>
    

    Here's how I called this class's functions:

    $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);
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥50 adb连接不到手机是怎么回事?
  • ¥15 vs2022无法联网
  • ¥15 TCP的客户端和服务器的互联
  • ¥15 VB.NET操作免驱摄像头
  • ¥15 笔记本上移动热点开关状态查询
  • ¥85 类鸟群Boids——仿真鸟群避障的相关问题
  • ¥15 CFEDEM自带算例错误,如何解决?
  • ¥15 有没有会使用flac3d软件的家人
  • ¥20 360摄像头无法解绑使用,请教解绑当前账号绑定问题,
  • ¥15 docker实践项目