如何在 PHP 中[递归]压缩目录?

目录类似于:

home/
file1.html
file2.html
Another_Dir/
file8.html
Sub_Dir/
file19.html

我使用的 PHP Zip 类与 PHPMyAdminhttp://trac.seagullproject.org/browser/branches/0.6-bugfix/lib/other/Zip.php中使用的相同。我不知道如何压缩目录而不仅仅是一个文件。以下是我目前掌握的信息:

$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

但是当我试图解压缩相应的下载的压缩文件,我得到“操作不允许”

这个错误只发生在我试图解压缩在我的 Mac,当我解压缩通过命令行的文件解压缩 OK。我是否需要在下载时发送特定的内容类型,目前是“ application/zip”

114427 次浏览

尝试 这个链接 < ——这里有更多的源代码

/** 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');
?>;

下面是一个简单的函数,它可以递归地压缩任何文件或目录,只需要加载 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();
}

Call it like this:

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

I've edited 阿里克斯 · 阿克塞尔's answer to take a third argrument, when setting this third argrument to true all the files will be added under the main directory rather than directly in the zip folder.

如果压缩文件存在,该文件也将被删除。

例如:

Zip('/path/to/maindirectory','/path/to/compressed.zip',true);

第三个参数 true拉链结构:

maindirectory
--- file 1
--- file 2
--- subdirectory 1
------ file 3
------ file 4
--- subdirectory 2
------ file 5
------ file 6

第三个参数 false或缺少拉链结构:

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

I needed to run this Zip function in Mac OSX

所以我总是把那个烦人的东西拉上。

我通过包含附加的 Ignore 文件改编了 https://stackoverflow.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/file’,’/path/to/pressed.zip’,false,array (’. DS _ Store’)) ;

另一个递归目录树归档,作为 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);

用法: Dir = ./path/to/file(压缩后启动 下载:)

<?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']);
}
?>

这里是我的代码邮政文件夹及其子文件夹和它的文件,使其可以下载的压缩文件格式

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

如果密码有任何问题,请告诉我。

伟大的解决方案,但我的 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();
}

This code works for both windows and 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();
}

下面是我的基于 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();
}

下面是一个简单易读的递归函数,它工作得非常好:

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

在@user2019515回答之后,我需要处理存档的排除问题。

Zip Function :

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

How to use it :

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