【问题标题】:How to zip a folder and download it using php?如何压缩文件夹并使用php下载?
【发布时间】:2011-03-30 09:14:03
【问题描述】:

我有一个名为“data”的文件夹。此“数据”文件夹包含一个文件“filecontent.txt”和另一个名为“文件”的文件夹。 “文件”文件夹包含一个“info.txt”文件。 所以它是文件夹结构中的一个文件夹。

我必须将这个文件夹“data”(使用 php)连同其中的文件和文件夹一起压缩,然后下载压缩文件。

我已经尝试了http://www.php.net/manual/en/zip.examples.php 提供的示例 这些例子不起作用。我的 PHP 版本是 5.2.10

请帮忙。

我已经写了这段代码。

<?php
$zip = new ZipArchive;
if ($zip->open('check/test2.zip',ZIPARCHIVE::CREATE) === TRUE) {
    if($zip->addEmptyDir('newDirectory')) {
        echo 'Created a new directory';
    } else {
        echo 'Could not create directory';
    }
    $zipfilename="test2.zip";
    $zipname="check/test2.zip";

    header('Content-Type: application/zip');
    header('Content-disposition: attachment; filename=check/test1.zip');    //header('Content-Length: ' . filesize( $zipfilename));
    readfile($zipname);  //$zip->close(); } else { echo failed';
}
?>

文件已下载但无法解压

【问题讨论】:

  • 你说的没有用是什么意思?
  • 我的意思是,当我在 url 处运行脚本时,不会发生错误,但不会发生压缩或下载。我不知道出了什么问题..

标签: php zipfile


【解决方案1】:

您需要在目录中递归添加文件。像这样的东西(未经测试):

function createZipFromDir($dir, $zip_file) {
    $zip = new ZipArchive;
    if (true !== $zip->open($zip_file, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)) {
        return false;
    }
    zipDir($dir, $zip);
    return $zip;
}

function zipDir($dir, $zip, $relative_path = DIRECTORY_SEPARATOR) {
    $dir = rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
    if ($handle = opendir($dir)) {
        while (false !== ($file = readdir($handle))) {
            if (file === '.' || $file === '..') {
                continue;
            }
            if (is_file($dir . $file)) {
                $zip->addFile($dir . $file, $file);
            } elseif (is_dir($dir . $file)) {
                zipDir($dir . $file, $zip, $relative_path . $file);
            }
        }
    }
    closedir($handle);
}

然后拨打$zip = createZipFromDir('/tmp/dir', 'files.zip');

为了获得更多胜利,我建议阅读 SPL DirectoryIterator here

【讨论】:

  • 感谢您的代码。我尝试过这个。但是当我在 url 处运行脚本时,不会发生错误,但不会发生压缩或下载。我不知道出了什么问题..
  • 代码压缩文件但不发送。您需要设置适当的标题(请参阅其他答案)然后调用fpassthru($zip_file)
  • 你在第 14 行犯了一个小错误,应该是 $file 而不是 file
【解决方案2】:

========= 对我来说唯一的解决方案! ! !==========

将所有子文件夹和子文件及其结构:

<?php
$the_folder = 'path/foldername';
$zip_file_name = 'archived_name.zip';


$download_file= true;
//$delete_file_after_download= true; doesnt work!!


class FlxZipArchive extends ZipArchive {
    /** Add a Dir with Files and Subdirs to the archive;;;;; @param string $location Real Location;;;;  @param string $name Name in Archive;;; @author Nicolas Heimann;;;; @access private  **/

    public function addDir($location, $name) {
        $this->addEmptyDir($name);

        $this->addDirDo($location, $name);
     } // EO addDir;

    /**  Add Files & Dirs to archive;;;; @param string $location Real Location;  @param string $name Name in Archive;;;;;; @author Nicolas Heimann
     * @access private   **/
    private function addDirDo($location, $name) {
        $name .= '/';
        $location .= '/';

        // Read all Files in Dir
        $dir = opendir ($location);
        while ($file = readdir($dir))
        {
            if ($file == '.' || $file == '..') continue;
            // Rekursiv, If dir: FlxZipArchive::addDir(), else ::File();
            $do = (filetype( $location . $file) == 'dir') ? 'addDir' : 'addFile';
            $this->$do($location . $file, $name . $file);
        }
    } // EO addDirDo();
}

$za = new FlxZipArchive;
$res = $za->open($zip_file_name, ZipArchive::CREATE);
if($res === TRUE) 
{
    $za->addDir($the_folder, basename($the_folder));
    $za->close();
}
else  { echo 'Could not create a zip archive';}

if ($download_file)
{
    ob_get_clean();
    header("Pragma: public");
    header("Expires: 0");
    header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
    header("Cache-Control: private", false);
    header("Content-Type: application/zip");
    header("Content-Disposition: attachment; filename=" . basename($zip_file_name) . ";" );
    header("Content-Transfer-Encoding: binary");
    header("Content-Length: " . filesize($zip_file_name));
    readfile($zip_file_name);

    //deletes file when its done...
    //if ($delete_file_after_download) 
    //{ unlink($zip_file_name); }
}
?>

【讨论】:

    【解决方案3】:

    几天前我不得不做同样的事情,这就是我所做的。

    1) 检索文件/文件夹结构并填充项目数组。每个项目要么是文件,要么是文件夹,如果是文件夹,则以相同的方式将其内容检索为项目。

    2) 解析该数组并生成 zip 文件。

    将我的代码放在下面,您当然必须根据应用程序的制作方式对其进行调整。

    // Get files
    $items['items'] = $this->getFilesStructureinFolder($folderId);
    
    $archiveName = $baseDir . 'temp_' . time(). '.zip';
    
    if (!extension_loaded('zip')) {
        dl('zip.so');
    }
    
    //all files added now
    $zip = new ZipArchive();
    $zip->open($archiveName, ZipArchive::OVERWRITE);
    
    $this->fillZipRecursive($zip, $items);
    
    $zip->close();
    
    //outputs file
    if (!file_exists($archiveName)) {
        error_log('File doesn\'t exist.');
        echo 'Folder is empty';
        return;
    }
    
    
    header("Pragma: public");
    header("Expires: 0");
    header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
    header("Cache-Control: private", false);
    header("Content-Type: application/zip");
    header("Content-Disposition: attachment; filename=" . basename($archiveName) . ";" );
    header("Content-Transfer-Encoding: binary");
    header("Content-Length: " . filesize($archiveName));
    readfile($archiveName);
    
    //deletes file when its done...
    unlink($archiveName);
    

    用于填充和解析的方法:

    /**
     * 
     * Gets all the files recursively within a folder and keeps the structure.
     * 
     * @param   int     $folderId   The id of the folder from which we start the search
     * @return  array   $tree       The data files/folders data structure within the given folder id
     */
    public function getFilesStructureinFolder($folderId) {
        $result = array();
    
        $query = $this->db->query('SELECT * FROM xx WHERE deleted = 0 AND status = 1 AND parent_folder_id = ? ORDER BY name ASC', $folderId);
    
        $folders = $query->result();
    
        foreach($folders as $folder) {
            $folderItem = array();
            $folderItem['type']     = 'folder';
            $folderItem['obj']      = $folder;  
            $folderItem['items']    = $this->getFilesStructureinFolder($folder->id);
            $result[]               = $folderItem;
        }
    
        $query = $this->db->query('SELECT * FROM xx WHERE deleted = 0 AND xx = ? AND status = 1 ORDER BY name ASC', $folderId);
    
        $files = $query->result();
    
        foreach ($files as $file) {
            $fileItem = array();
            $fileItem['type']   = 'file';
            $fileItem['obj']    = $file;    
            $result[]           = $fileItem;
        }
    
        return $result;
    }
    
    /**
     * Fills zip file recursively
     * 
     * @param ZipArchive    $zip        The zip archive we are filling
     * @param Array         $items      The array representing the file/folder structure
     * @param String        $zipPath    Local path within the zip
     * 
     */
    public function fillZipRecursive($zip, $items, $zipPath = '') {
        $baseDir = $this->CI->config->item('xxx');
    
        foreach ($items['items'] as $item) {
    
            //Item is a file
            if ($item['type'] == 'file') {
                $file = $item['obj'];
                $fileName = $baseDir . '/' . $file->fs_folder_id . '/' . $file->file_name;
    
                if (trim($file->file_name) == '' || !file_exists($fileName))
                    continue;
    
                $zip->addFile($fileName, $zipPath.''.$file->file_name);
            }
    
            //Item is a folder
            else if ($item['type'] == 'folder') {
                $folder     = $item['obj'];
    
                $zip->addEmptyDir($zipPath.''.$folder->name);
    
                //Folder probably has items in it!
                if (!empty($item['items']))
                    $this->fillZipRecursive($zip, $item, $zipPath.'/'.$folder->name.'/');
            }
        }
    } 
    

    【讨论】:

    • 我已经写了这段代码。 $zip = 新的 ZipArchive; if ($zip->open('check/test2.zip',ZIPARCHIVE::CREATE) === TRUE) { if($zip->addEmptyDir('newDirectory')) { echo '创建了一个新目录'; } else { echo '无法创建目录'; } $zipfilename="test2.zip"; $zipname="check/test2.zip"; header('Content-Type: application/zip'); header('内容配置:附件;文件名=check/test1.zip'); //header('Content-Length:' .filesize($zipfilename));读取文件($zipname); //$zip->close(); } 其他 { 回声失败'; } 文件已下载但无法解压缩
    • 在使用header之前不要使用echo,如果需要调试信息可以使用error_log()。
    【解决方案4】:

    查看链接的重复项。另一个经常被忽视且特别懒惰的选择是:

    exec("zip -r data.zip data/");
    header("Content-Type: application/zip");
    readfile("data.zip");    // must be a writeable location though
    

    【讨论】:

    • 懒惰?加入一些平台检测并称之为简短而甜蜜:D
    • 由于安全原因,在大多数托管服务器上,PHP 中禁用了 exec。
    • @FractalizeR:我不会称它们为“最”,而是“低端”。而且它也不是很好的安全方法的指示性。 (我还没有看到你无法通过将自己的 PHP 解释器放入 cgi-bin 来规避它。)
    • 我试过这段代码。当我打开下载的文件时,它会下载但文件中没有数据。我用的是mac系统。这会是个问题吗?
    • @Sangam254:是的。它需要安装zip 命令行工具。它也有多种变体。如果您有 Mac 版本,它可能需要不同的参数。 (另外:注意输出文件在任何情况下都必须位于可写位置。)
    【解决方案5】:

    使用 TbsZip 类创建一个新的 zip 存档。 TbsZip 很简单,它不使用临时文件,不使用 zip EXE,它没有依赖项,并且具有将存档刷新为下载文件的下载功能。

    您只需在文件夹树下循环并添加存档中的所有文件,然后刷新它。

    代码示例:

    $zip = new clsTbsZip(); // instantiate the class
    $zip->CreateNew(); // create a virtual new zip archive
    foreach (...) { // your loop to scann the folder tree
      ...
      // add the file in the archive
      $zip->FileAdd($FileInnerName, $LocalFilePath, TBSZIP_FILE);
    }
    // flush the result as an HTTP download
    $zip->Flush(TBSZIP_DOWNLOAD, 'my_archive.zip');
    

    存档中添加的文件将在 Flush() 方法期间按顺序压缩。所以你的存档可以包含很多子文件,这不会增加 PHP 内存。

    【讨论】:

    • 爱它!使用起来非常简单,并且适用于 GoDaddy 或 OVH 等共享(廉价)托管服务
    猜你喜欢
    • 1970-01-01
    • 2011-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-22
    相关资源
    最近更新 更多