如何[递归地]压缩PHP中的目录? [复制]
Posted
技术标签:
【中文标题】如何[递归地]压缩PHP中的目录? [复制]【英文标题】:How to [recursively] Zip a directory in PHP? [duplicate] 【发布时间】:2010-11-23 00:08:54 【问题描述】:目录类似于:
home/
file1.html
file2.html
Another_Dir/
file8.html
Sub_Dir/
file19.html
我正在使用与 phpMyAdmin http://trac.seagullproject.org/browser/branches/0.6-bugfix/lib/other/Zip.php 中相同的 PHP Zip 类。我不确定如何压缩目录而不仅仅是文件。到目前为止,这是我所拥有的:
$aFiles = $this->da->getDirTree($target);
/* $aFiles is something like, path => filetime
Array
(
[home] =>
[home/file1.html] => 1251280379
[home/file2.html] => 1251280377
etc...
)
*/
$zip = & new Zip();
foreach( $aFiles as $fileLocation => $time )
$file = $target . "/" . $fileLocation;
if ( is_file($file) )
$buffer = file_get_contents($file);
$zip->addFile($buffer, $fileLocation);
THEN_SOME_PHP_CLASS::toDownloadData($zip); // this bit works ok
但是当我尝试解压缩相应下载的 zip 文件时,我得到“不允许操作”
此错误仅在我尝试在我的 mac 上解压缩时发生,当我通过命令行解压缩文件时,文件解压缩正常。我是否需要在下载时发送特定的内容类型,目前是“应用程序/压缩包”
【问题讨论】:
这段代码确实有效 - 但由于某种原因,您无法在 Mac OS 上解压缩它(除非您使用 CLI 解压缩)。 Zip 文件在 PC 上没问题。 这可以帮助你codingbin.com/compressing-a-directory-of-files-with-php 【参考方案1】:在@user2019515 回答之后,我需要处理对我的存档的排除。这是带有示例的结果函数。
邮编功能:
function Zip($source, $destination, $include_dir = false, $exclusions = false)
// Remove existing archive
if (file_exists($destination))
unlink ($destination);
$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);
if ($include_dir)
$arr = explode("/",$source);
$maindir = $arr[count($arr)- 1];
$source = "";
for ($i=0; $i < count($arr) - 1; $i++)
$source .= '/' . $arr[$i];
$source = substr($source, 1);
$zip->addEmptyDir($maindir);
foreach ($files as $file)
// Ignore "." and ".." folders
$file = str_replace('\\', '/', $file);
if(in_array(substr($file, strrpos($file, '/')+1), array('.', '..')))
continue;
// Add Exclusion
if(($exclusions)&&(is_array($exclusions)))
if(in_array(str_replace($source.'/', '', $file), $exclusions))
continue;
$file = realpath($file);
if (is_dir($file) === true)
$zip->addEmptyDir(str_replace($source . '/', '', $file . '/'));
elseif (is_file($file) === true)
$zip->addFromString(str_replace($source . '/', '', $file), file_get_contents($file));
elseif (is_file($source) === true)
$zip->addFromString(basename($source), file_get_contents($source));
return $zip->close();
如何使用它:
function backup()
$backup = 'tmp/backup-'.$this->site['version'].'.zip';
$exclusions = [];
// Excluding an entire directory
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('tmp/'), RecursiveIteratorIterator::SELF_FIRST);
foreach ($files as $file)
array_push($exclusions,$file);
// Excluding a file
array_push($exclusions,'config/config.php');
// Excluding the backup file
array_push($exclusions,$backup);
$this->Zip('.',$backup, false, $exclusions);
【讨论】:
【参考方案2】:这是一个简单、易读、运行良好的递归函数:
function zip_r($from, $zip, $base=false)
if (!file_exists($from) OR !extension_loaded('zip')) return false;
if (!$base) $base = $from;
$base = trim($base, '/');
$zip->addEmptyDir($base);
$dir = opendir($from);
while (false !== ($file = readdir($dir)))
if ($file == '.' OR $file == '..') continue;
if (is_dir($from . '/' . $file))
zip_r($from . '/' . $file, $zip, $base . '/' . $file);
else
$zip->addFile($from . '/' . $file, $base . '/' . $file);
return $zip;
$from = "/path/to/folder";
$base = "basezipfolder";
$zip = new ZipArchive();
$zip->open('zipfile.zip', ZIPARCHIVE::CREATE);
$zip = zip_r($from, $zip, $base);
$zip->close();
【讨论】:
【参考方案3】:这是我基于 Alix 的版本,适用于 Windows,希望也适用于 *nix:
function addFolderToZip($source, $destination, $flags = ZIPARCHIVE::OVERWRITE)
$source = realpath($source);
$destination = realpath($destination);
if (!file_exists($source))
die("file does not exist: " . $source);
$zip = new ZipArchive();
if (!$zip->open($destination, $flags ))
die("Cannot open zip archive: " . $destination);
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST);
$sourceWithSeparator = $source . DIRECTORY_SEPARATOR;
foreach ($files as $file)
// Ignore "." and ".." folders
if(in_array(substr($file,strrpos($file, DIRECTORY_SEPARATOR)+1),array('.', '..')))
continue;
if (is_dir($file) === true)
$zip->addEmptyDir(
str_replace($sourceWithSeparator, '', $file . DIRECTORY_SEPARATOR));
else if (is_file($file) === true)
$zip->addFile($file, str_replace($sourceWithSeparator, '', $file));
return $zip->close();
【讨论】:
【参考方案4】:此代码适用于 windows 和 linux。
function Zip($source, $destination)
if (!extension_loaded('zip') || !file_exists($source))
return false;
$zip = new ZipArchive();
if (!$zip->open($destination, ZIPARCHIVE::CREATE))
return false;
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN')
DEFINE('DS', DIRECTORY_SEPARATOR); //for windows
else
DEFINE('DS', '/'); //for linux
$source = str_replace('\\', DS, realpath($source));
if (is_dir($source) === true)
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST);
echo $source;
foreach ($files as $file)
$file = str_replace('\\',DS, $file);
// Ignore "." and ".." folders
if( in_array(substr($file, strrpos($file, DS)+1), array('.', '..')) )
continue;
$file = realpath($file);
if (is_dir($file) === true)
$zip->addEmptyDir(str_replace($source . DS, '', $file . DS));
else if (is_file($file) === true)
$zip->addFromString(str_replace($source . DS, '', $file), file_get_contents($file));
echo $source;
else if (is_file($source) === true)
$zip->addFromString(basename($source), file_get_contents($source));
return $zip->close();
【讨论】:
【参考方案5】:用法:thisfile.php?dir=./path/to/folder(压缩后也开始下载:)
<?php
$exclude_some_files=
array(
'mainfolder/folder1/filename.php',
'mainfolder/folder5/otherfile.php'
);
//***************built from https://gist.github.com/ninadsp/6098467 ******
class ModifiedFlxZipArchive extends ZipArchive
public function addDirDoo($location, $name , $prohib_filenames=false)
if (!file_exists($location)) die("maybe file/folder path incorrect");
$this->addEmptyDir($name);
$name .= '/';
$location.= '/';
$dir = opendir ($location); // Read all Files in Dir
while ($file = readdir($dir))
if ($file == '.' || $file == '..') continue;
if (!in_array($name.$file,$prohib_filenames))
if (filetype( $location . $file) == 'dir')
$this->addDirDoo($location . $file, $name . $file,$prohib_filenames );
else
$this->addFile($location . $file, $name . $file);
public function downld($zip_name)
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_name) . ";" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . filesize($zip_name));
readfile($zip_name);
//set memory limits
set_time_limit(3000);
ini_set('max_execution_time', 3000);
ini_set('memory_limit','100M');
$new_zip_filename='down_zip_file_'.rand(1,1000000).'.zip';
// Download action
if (isset($_GET['dir']))
$za = new ModifiedFlxZipArchive;
//create an archive
if ($za->open($new_zip_filename, ZipArchive::CREATE))
$za->addDirDoo($_GET['dir'], basename($_GET['dir']), $exclude_some_files); $za->close();
else die('cantttt');
if (isset($_GET['dir']))
$za = new ModifiedFlxZipArchive;
//create an archive
if ($za->open($new_zip_filename, ZipArchive::CREATE))
$za->addDirDoo($_GET['dir'], basename($_GET['dir']), $exclude_some_files); $za->close();
else die('cantttt');
//download archive
//on the same execution,this made problems in some hostings, so better redirect
//$za -> downld($new_zip_filename);
header("location:?fildown=".$new_zip_filename); exit;
if (isset($_GET['fildown']))
$za = new ModifiedFlxZipArchive;
$za -> downld($_GET['fildown']);
?>
【讨论】:
【参考方案6】:很好的解决方案,但对于我的 Windows,我需要进行修改。下面修改代码
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;
if (is_dir($file) === true)
$zip->addEmptyDir(str_replace($source . '/', '', $file));
else if (is_file($file) === true)
$str1 = str_replace($source . '/', '', '/'.$file);
$zip->addFromString($str1, file_get_contents($file));
else if (is_file($source) === true)
$zip->addFromString(basename($source), file_get_contents($source));
return $zip->close();
【讨论】:
【参考方案7】:这是我的代码,用于 Zip 文件夹及其子文件夹及其文件,并使其以 zip 格式下载
function zip()
$source='path/folder'// Path To the folder;
$destination='path/folder/abc.zip'// Path to the file and file name ;
$include_dir = false;
$archive = 'abc.zip'// File Name ;
if (!extension_loaded('zip') || !file_exists($source))
return false;
if (file_exists($destination))
unlink ($destination);
$zip = new ZipArchive;
if (!$zip->open($archive, ZipArchive::CREATE))
return false;
$source = str_replace('\\', '/', realpath($source));
if (is_dir($source) === true)
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST);
if ($include_dir)
$arr = explode("/",$source);
$maindir = $arr[count($arr)- 1];
$source = "";
for ($i=0; $i < count($arr) - 1; $i++)
$source .= '/' . $arr[$i];
$source = substr($source, 1);
$zip->addEmptyDir($maindir);
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->addFromString(str_replace($source . '/', '', $file), file_get_contents($file));
else if (is_file($source) === true)
$zip->addFromString(basename($source), file_get_contents($source));
$zip->close();
header('Content-Type: application/zip');
header('Content-disposition: attachment; filename='.$archive);
header('Content-Length: '.filesize($archive));
readfile($archive);
unlink($archive);
如果代码有任何问题,请告诉我。
【讨论】:
【参考方案8】:又一个递归目录树归档,作为 ZipArchive 的扩展实现。作为奖励,包括一个单语句树压缩辅助函数。与其他 ZipArchive 函数一样,支持可选的 localname。错误处理代码待添加...
class ExtendedZip extends ZipArchive
// Member function to add a whole file system subtree to the archive
public function addTree($dirname, $localname = '')
if ($localname)
$this->addEmptyDir($localname);
$this->_addTree($dirname, $localname);
// Internal function, to recurse
protected function _addTree($dirname, $localname)
$dir = opendir($dirname);
while ($filename = readdir($dir))
// Discard . and ..
if ($filename == '.' || $filename == '..')
continue;
// Proceed according to type
$path = $dirname . '/' . $filename;
$localpath = $localname ? ($localname . '/' . $filename) : $filename;
if (is_dir($path))
// Directory: add & recurse
$this->addEmptyDir($localpath);
$this->_addTree($path, $localpath);
else if (is_file($path))
// File: just add
$this->addFile($path, $localpath);
closedir($dir);
// Helper function
public static function zipTree($dirname, $zipFilename, $flags = 0, $localname = '')
$zip = new self();
$zip->open($zipFilename, $flags);
$zip->addTree($dirname, $localname);
$zip->close();
// Example
ExtendedZip::zipTree('/foo/bar', '/tmp/archive.zip', ZipArchive::CREATE);
【讨论】:
很好的答案乔治!在树结构的窗口上,它比 Zip() 提供更好的结果。谢谢【参考方案9】:我需要在 Mac OSX 中运行这个 Zip 函数
所以我总是会压缩那个烦人的 .DS_Store。
我通过包含额外的忽略文件来适应 https://***.com/users/2019515/user2019515。
function zipIt($source, $destination, $include_dir = false, $additionalIgnoreFiles = array())
// Ignore "." and ".." folders by default
$defaultIgnoreFiles = array('.', '..');
// include more files to ignore
$ignoreFiles = array_merge($defaultIgnoreFiles, $additionalIgnoreFiles);
if (!extension_loaded('zip') || !file_exists($source))
return false;
if (file_exists($destination))
unlink ($destination);
$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);
if ($include_dir)
$arr = explode("/",$source);
$maindir = $arr[count($arr)- 1];
$source = "";
for ($i=0; $i < count($arr) - 1; $i++)
$source .= '/' . $arr[$i];
$source = substr($source, 1);
$zip->addEmptyDir($maindir);
foreach ($files as $file)
$file = str_replace('\\', '/', $file);
// purposely ignore files that are irrelevant
if( in_array(substr($file, strrpos($file, '/')+1), $ignoreFiles) )
continue;
$file = realpath($file);
if (is_dir($file) === true)
$zip->addEmptyDir(str_replace($source . '/', '', $file . '/'));
else if (is_file($file) === true)
$zip->addFromString(str_replace($source . '/', '', $file), file_get_contents($file));
else if (is_file($source) === true)
$zip->addFromString(basename($source), file_get_contents($source));
return $zip->close();
所以要忽略 zip 中的 .DS_Store,请运行
zipIt('/path/to/folder', '/path/to/compressed.zip', false, array('.DS_Store'));
【讨论】:
【参考方案10】:我已编辑Alix Axel 的答案以采用第三个参数,当将此第三个参数设置为true
时,所有文件都将添加到主目录下,而不是直接添加到 zip 文件夹中。
如果 zip 文件存在,该文件也将被删除。
例子:
Zip('/path/to/maindirectory','/path/to/compressed.zip',true);
第三个参数true
zip 结构:
maindirectory
--- file 1
--- file 2
--- subdirectory 1
------ file 3
------ file 4
--- subdirectory 2
------ file 5
------ file 6
第三个参数 false
或缺少 zip 结构:
file 1
file 2
subdirectory 1
--- file 3
--- file 4
subdirectory 2
--- file 5
--- file 6
修改后的代码:
function Zip($source, $destination, $include_dir = false)
if (!extension_loaded('zip') || !file_exists($source))
return false;
if (file_exists($destination))
unlink ($destination);
$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);
if ($include_dir)
$arr = explode("/",$source);
$maindir = $arr[count($arr)- 1];
$source = "";
for ($i=0; $i < count($arr) - 1; $i++)
$source .= '/' . $arr[$i];
$source = substr($source, 1);
$zip->addEmptyDir($maindir);
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->addFromString(str_replace($source . '/', '', $file), file_get_contents($file));
else if (is_file($source) === true)
$zip->addFromString(basename($source), file_get_contents($source));
return $zip->close();
【讨论】:
谢谢!我需要在我的情况下包含主目录。 你的功能不工作只有主(根)目录正在添加,什么都没有 我知道这是很久以前的回答。是否可以为“主目录”使用自定义名称而不是原始名称。 @VaibhavSidapara 相信可以通过将$maindir
更改为首选名称来实现。
很好的答案,对我帮助很大。我确实为该函数添加了第四个参数以包含排除项。我将添加最终代码作为这个问题的另一个答案。【参考方案11】:
这是一个简单的函数,可以递归压缩任何文件或目录,只需要加载zip扩展。
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->addFromString(str_replace($source . '/', '', $file), file_get_contents($file));
else if (is_file($source) === true)
$zip->addFromString(basename($source), file_get_contents($source));
return $zip->close();
这样称呼它:
Zip('/folder/to/compress/', './compressed.zip');
【讨论】:
工作得很好,我唯一的问题是我的脚本从不同的位置运行到要压缩的文件,因此当我提供第一个参数时,在 zip 中使用完整的文件路径位置,就像这样: C:\wamp\www\export\pkg-1211.191011\pkg-1211.191011.zip,完整的嵌套文件夹结构位于新存档中。有没有办法让上面的脚本只包含我指向的文件和目录,而不是它们来自的完整路径? @Danjah:我已经更新了代码,现在应该适用于 *nix 和 Windows。 我想知道为什么这是使用file_get_contents
并添加字符串。 zip不支持直接添加文件吗?
当然,您必须将所有 '/'
替换为 DIRECTORY_SEPARATOR
才能使其在 Windows 上运行。否则,您最终会得到 ZIP 中的完整路径(包括驱动器名称),例如C:\Users\...
.
原始代码已损坏且冗余。无需将//
替换为\
,因为这实际上破坏了windows 上的foreach。如果您使用内置的DIRECTORY_SEPARATOR
,则无需更换。 /
的硬编码是导致一些用户遇到问题的原因。我有点困惑为什么我得到一个空档案。我的修订版可以在 *nix 和 Windows 下正常运行。【参考方案12】:
试试this link
/** Include the Pear Library for Zip */
include ('Archive/Zip.php');
/** Create a Zipping Object...
* Name of zip file to be created..
* You can specify the path too */
$obj = new Archive_Zip('test.zip');
/**
* create a file array of Files to be Added in Zip
*/
$files = array('black.gif',
'blue.gif',
);
/**
* creating zip file..if success do something else do something...
* if Error in file creation ..it is either due to permission problem (Solution: give 777 to that folder)
* Or Corruption of File Problem..
*/
if ($obj->create($files))
// echo 'Created successfully!';
else
//echo 'Error in file creation';
?>; // We'll be outputting a ZIP
header('Content-type: application/zip');
// It will be called test.zip
header('Content-Disposition: attachment; filename="test.zip"');
//read a file and send
readfile('test.zip');
?>;
【讨论】:
以上是关于如何[递归地]压缩PHP中的目录? [复制]的主要内容,如果未能解决你的问题,请参考以下文章
如何在 PHP 中递归删除目录及其全部内容(文件 + 子目录)? [复制]
php ZipArchive 压缩整个文件夹 - 自带ZipArchive类 - PHP递归创建目录压缩包