Reputation: 27
I want to create a zip file and copy all the folders and files from a directory to it. It is successfully created and contains the files and folders, but the file tree is not preserved, everything being in the root directory.
My directory:
folder/
test.txt
test2.txt
test.php
The zip archive:
folder/
test.txt
test2.txt
test.php
This is my code:
public function createZipFromDir($dir, $zip_file) {
$zip = new ZipArchive();
if(true !== $zip->open($zip_file, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)) {
return false;
}
$this->zipDir($dir, $zip);
return $zip;
}
public function zipDir($dir, $zip) {
$dir = rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
$files = scandir($dir);
foreach($files as $file) {
if(in_array($file, array('.', '..'))) continue;
if(is_dir($dir . $file)) {
$zip->addEmptyDir($file);
$this->zipDir($dir . $file, $zip);
} else {
$zip->addFile($dir . $file, $file);
}
}
}
$zip = $this->createZipFromDir($rootPath, $archiveName);
Upvotes: 0
Views: 2378
Reputation: 4409
The issue is that when you create a folder or set the localname
(second argument of addFile()
) when adding a file to the archive, you only use $file
, therefore everything gets put at the root. It is necessary to provide the file hierarchy as well.
Now the obvious solution would be to use $dir.$file
instead, but this would only work properly on a folder located in the same directory as the script.
We actually need to keep track of two file trees:
But since one is just a subset of the other, we can easily keep track of that by splitting the real path in two:
$dir
, a prefix pointing to the original path$subdir
, a path relative to $dir
When referring to a file on the machine, we use $dir.$subdir
and when referring to a file in the archive we use only $subdir
. This requires us to adapt zipDir()
to keep track of the prefix by adding a third argument to it and slightly modifying the call to zipDir()
in createZipFromDir()
.
function createZipFromDir($dir, $zip_file) {
$zip = new ZipArchive();
if(true !== $zip->open($zip_file, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)) {
return false;
}
zipDir(
// base dir, note we use a trailing separator from now on
rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR,
// subdir, empty on initial call
null,
// archive ref
$zip
);
return $zip;
}
function zipDir($dir, $subdir, $zip) {
// using real path
$files = scandir($dir.$subdir);
foreach($files as $file) {
if(in_array($file, array('.', '..')))
continue;
// check dir using real path
if(is_dir($dir.$subdir.$file)) {
// create folder using relative path
$zip->addEmptyDir($subdir.$file);
zipDir(
$dir, // remember base dir
$subdir.$file.DIRECTORY_SEPARATOR, // relative path, don't forget separator
$zip // archive
);
}
// file
else {
// get real path, set relative path
$zip->addFile($dir.$subdir.$file, $subdir.$file);
}
}
}
This code has been tested and is working.
Upvotes: 1