Creating ZIP and TAR archives on the fly with PHP
(Page 3 out of 5)Fixing the relative paths
So we have to find a solution for that. The easiest solution is to simply copy all the files and directory to our temporary directory, so the relative paths still work. Copying a file is easy, since PHP comes with an inbuilt copy function logically called copy(). We have to add the following to our script:
foreach ($list as $item) {
if (is_file($item)) {
// Copy file
copy ($item, $dir . $item);
} elseif (is_dir ($item)) {
// TODO: we'll do this in a minute
} else {
// Invalid file, just ignore
continue;
}
}
If the above code is placed above the chdir() call, all the files will be included in the zip file, and work properly. But directories aren't yet included because we haven't copied those yet.
Unfortunately PHP doesn't come with a copy_directory() or something similar, and we have to write our own. A quick Google search will probably turn up something very quickly, but here's one I wrote myself:
// Standardize paths
$source = str_replace('\\\\', '/', $source);
$source = str_replace('\\', '/', $source);
$dest = str_replace('\\\\', '/', $dest);
$dest = str_replace('\\', '/', $dest);
// Remove trailing slashes
$source = rtrim($source, '/');
$dest = rtrim($dest, '/');
// Add trailing slashes
$source .= '/';
$dest .= '/';
// Source doesn't exist?
if (file_exists($source) == false) { return false; }
// Try to create destination
@mkdir($dest);
// Destination doesn't exist?
if (file_exists($dest) == false) { return false; }
// Copy all files
$d = dir($source);
while (false !== ($entry = $d->read())) {
if ($entry == '.' OR $entry == '..') continue;
$file = $d->path . $entry;
// Is a sub dir?
if (is_dir($file)) {
// Try to create sub dir
$subdir = $dest . $entry . '/';
$result = @mkdir($subdir);
if ($result == false) { continue; }
// Move files in sub directory
move_directory($file, $subdir, $copy);
continue;
}
// Copy file
copy($file, $dest . $entry);
// Remove old file?
if ($copy == false) {
// Moving the directory, so remove original
@unlink($file);
}
}
if ($copy == false) {
@rmdir($source);
}
return true;
}
I won't really bother explaining the function, but it basically copies all the files in the directory, and all the sub-directories (and does the same for all the sub-directories, and so forth).
If we incorporate this function into our script, the part that copies the files and directories now looks like this:
foreach ($list as $item) {
if (is_file($item)) {
// Copy file
copy ($item, $dir . $item);
} elseif (is_dir ($item)) {
// Copy directory
move_directory ($item, $dir . $item, true);
} else {
// Invalid file, just ignore
continue;
}
}
And if we put everything together our script looks like this:
include ('pear/archive_zip.php');
// Create instance of Archive_Zip class, and pass the name of our zipfile
$zipfile = New Archive_Zip('myzipfile.zip');
// Create a list of files and directories
$list = array('example.txt', 'test');
// Get temporary directory
if (!empty($_ENV['TMP'])) {
$tempdir = $_ENV['TMP'];
} elseif (!empty($_ENV['TMPDIR'])) {
$tempdir = $_ENV['TMPDIR'];
} elseif (!empty($_ENV['TEMP'])) {
$tempdir = $_ENV['TEMP'];
} else {
$tempdir = dirname(tempnam('', 'na'));
}
if (empty($tempdir)) { die ('No temporary directory'); }
// Make sure trailing slash is there
$tempdir = rtrim($tempdir, '/');
$tempdir .= '/';
// Make sure temporary directory is writable
if (is_writable($tempdir) == false) {
die ('Temporary directory isn\'t writable');
}
// Create temp name for our own directory
$dir = tempnam($tempdir, 'temp');
// Make sure another file or directory doesn't already exist with this name
@unlink($dir);
@rmdir($dir);
// Create directory
mkdir($dir);
$dir .= '/';
// Copy files & directories so relative paths still work
foreach ($list as $item) {
if (is_file($item)) {
// Copy file
copy ($item, $dir . $item);
} elseif (is_dir ($item)) {
// Copy directory
move_directory ($item, $dir . $item, true);
} else {
// Invalid file, just ignore
continue;
}
}
// Change current working directory, so that the zip file gets created in the temp dir
chdir($dir);
// Create the zip file
$zipfile->create($list);
echo 'Zip file created in ' . $dir;
function move_directory($source, $dest, $copy=false) {
// Standardize paths
$source = str_replace('\\\\', '/', $source);
$source = str_replace('\\', '/', $source);
$dest = str_replace('\\\\', '/', $dest);
$dest = str_replace('\\', '/', $dest);
// Remove trailing slashes
$source = rtrim($source, '/');
$dest = rtrim($dest, '/');
// Add trailing slashes
$source .= '/';
$dest .= '/';
// Source doesn't exist?
if (file_exists($source) == false) { return false; }
// Try to create destination
@mkdir($dest);
// Destination doesn't exist?
if (file_exists($dest) == false) { return false; }
// Copy all files
$d = dir($source);
while (false !== ($entry = $d->read())) {
if ($entry == '.' OR $entry == '..') continue;
$file = $d->path . $entry;
// Is a sub dir?
if (is_dir($file)) {
// Try to create sub dir
$subdir = $dest . $entry . '/';
$result = @mkdir($subdir);
if ($result == false) { continue; }
// Move files in sub directory
move_directory($file, $subdir, $copy);
continue;
}
// Copy file
copy($file, $dest . $entry);
// Remove old file?
if ($copy == false) {
// Moving the directory, so remove original
@unlink($file);
}
}
if ($copy == false) {
@rmdir($source);
}
return true;
}
?>
And that's pretty much all we need to do.
May 18th, 2006 at 12:34 am
[…] Link: http://phpit.net/article/creating-zip-tar-archives-dynamically-php/ […]
May 18th, 2006 at 12:36 am
[…] Link: http://phpit.net/article/creating-zip-tar-archives-dynamically-php/ Posted by Administrator on May 18th, 2006 Filed in tutorials, php, links, php […]
May 20th, 2006 at 6:03 am
[…] http://phpit.net/article/creating-zip-tar-archives-dynamically-php/ […]
June 12th, 2006 at 9:00 am
Hi,
This was very interesting and gave me lots of thoughs to create new things,,
thanks alot
July 5th, 2006 at 3:13 am
Hi, Thanks a lot for the detailed tutorial… I have one problem, although: When I executed your script, it simply downloads the actual “example.txt” file (zipped up) instead of the array of files. I’m very new to php so apologies for my ignorance in advance but is there something I’m missing?
July 23rd, 2006 at 5:23 pm
Hi, this is Andrew again and I am still having the same problem almost 3 weeks later. If I implement the script exactly “As is”, it runs perfectly fine and I download the zip file but it only has 1 file in it… the “example.txt” file. Any ideas? I can’t find any other zip file tutorials…
August 10th, 2006 at 2:00 pm
Just tried it and it worked fine, very good article. First it didn’t put any file in the zipfile altough i filed my filelist array correctly, but later I saw it was due to a mistake in filepaths (I forgot a directory in the path).
Andrew ->Did you check that your filepaths are OK?