如何压缩文件夹并使用php下载?

Posted

技术标签:

【中文标题】如何压缩文件夹并使用php下载?【英文标题】:How to zip a folder and download it using 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';

?>

文件已下载但无法解压

【问题讨论】:

你说的没有用是什么意思? How to zip a whole folder using PHP的可能重复 我的意思是,当我在 url 处运行脚本时,不会发生错误,但不会发生压缩或下载。我不知道出了什么问题.. 【参考方案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 等共享(廉价)托管服务

以上是关于如何压缩文件夹并使用php下载?的主要内容,如果未能解决你的问题,请参考以下文章

php导出excel表后,打包成压缩包,然后下载到本地如何实现?

如何解压缩txt.gz文件并使用php存储到数据库中

如何使用 Javascript 下载、压缩和保存多个文件并取得进展?

文件打包,下载之使用PHP自带的ZipArchive压缩文件并下载打包好的文件

使用 php 压缩大文件 [关闭]

如何使用 JavaScript 压缩文件?