Problem
A directory looks like this:
home/
file1.html
file2.html
Another_Dir/
file8.html
Sub_Dir/
file19.html
I’m using the same PHP Zip class that PHPMyAdmin uses: http://trac.seagullproject.org/browser/branches/0.6-bugfix/lib/other/Zip.php I don’t know how to compress a directory instead of a single file. Here’s what I’ve come up with thus far:
$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
However, when I try to unzip the downloaded zip file, I receive the message “operation not permitted.”
This problem only occurs when I try to unzip on my Mac; the file unzips OK when I use the command line. Is it necessary to send a specific content type (currently ‘application/zip’) while sending a download?
Asked by ed209
Solution #1
Here’s a simple function that can recursively compress any file or directory, all you need is the zip extension loaded.
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();
}
It’s referred to as:
Zip('/folder/to/compress/', './compressed.zip');
Answered by Alix Axel
Solution #2
Another extension to ZipArchive, this time for recursive directory tree archiving. A single-statement tree compression assistance function is given as a bonus. As with other ZipArchive methods, localname is optional. Code to handle errors will be added…
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);
Answered by Giorgio Barchiesi
Solution #3
When the third argument is set to true, all files will be added to the main directory rather than the zip folder.
If the zip file already exists, it will be erased.
Example:
Zip('/path/to/maindirectory','/path/to/compressed.zip',true);
True zip structure of the third argument:
maindirectory
--- file 1
--- file 2
--- subdirectory 1
------ file 3
------ file 4
--- subdirectory 2
------ file 5
------ file 6
False or missing zip structure in the third argument:
file 1
file 2
subdirectory 1
--- file 3
--- file 4
subdirectory 2
--- file 5
--- file 6
Edited code:
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();
}
Answered by user2019515
Solution #4
USAGE: thisfile.php?dir=./folder/path/to (It also starts downloading after zipping:)
<?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']);
}
?>
Answered by T.Todua
Solution #5
Take a look at this link— CLICK HERE FOR MORE SOURCE CODE
/** 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');
?>;
Answered by Phill Pafford
Post is based on https://stackoverflow.com/questions/1334613/how-to-recursively-zip-a-directory-in-php