I have a function which is working fine to create zip file from folder files. But recently I've had need to add sub-folders into my main folder and now I see my function does not add those sub-folders and files in them into generated zip file.
here is what I have currently:
$zip = new ZipArchive;
if ($zip->open(public_path('Downloads/new_zip.zip'), ZipArchive::CREATE) === TRUE)
{
$files = File::files(public_path('new_zip'), true);
foreach ($files as $key => $value) {
$relativeNameInZipFile = basename($value);
$zip->addFile($value, $relativeNameInZipFile);
}
$zip->close();
}
By using code above, let say I have following structure:
new_zip
sample.txt
It works fine to create zip file for my folder.
But
If my folder structure is like:
new_zip
sample.txt
folder_a
file_a.txt
folder_b
folder_c
file_c.txt
Then it ignores everything from folder_a and beyond.
Any suggestions?
You can use this method
The 1st argument is the path to the directory whose data you want to compress
The 2nd argument is the path to the resulting zip file
for your case:
createZipArchive(public_path('new_zip'), public_path('Downloads/new_zip.zip'))
function createZipArchive(string $sourceDirPath, string $resultZipFilePath): bool
{
$zip = new ZipArchive();
if (true !== $zip->open($resultZipFilePath, ZipArchive::CREATE | ZipArchive::OVERWRITE)) {
return false;
}
/** #var SplFileInfo[] $files */
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($sourceDirPath),
RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($files as $file) {
$filePath = $file->getRealPath();
if ($file->isDir() || !$filePath) {
continue;
}
$relativePath = substr($filePath, strlen($sourceDirPath) + 1);
$zip->addFile($filePath, $relativePath);
}
return $zip->close();
}
This method will fully reproduce the folder structure of the source directory.
and a little bit of clarification:
To add the directory "test_dir" and the file "test.txt" to the archive - you just need to do:
$zip->addFile($filePath, "test_dir/test.txt");
The RecursiveDirectoryIterator and RecursiveIteratorIterator are used to recursively traverse the directories of the source folder. They are part of the standard php library. You can read about them in the official php documentation
Related
I am working on a project which requires me to collect several files from the server, zip them, and mount the zip as a drive with js-dos.
Everything works 'correctly'. During testing I manually made a zip file in windows and the code ran. For the final test I tried to mount the php generated zip but the folder structure is strange when doing DIR in js-dos.
I should have a single folder with several files in it. Instead there are several of the same folders with a single file inside.
The thing that breaks my brain is that when I open the file in winRAR it's correct, but in js-dos it's suddenly different.
Here is my code, it's nothing fancy:
$rootPath = realpath($filefoldername."/");
$zip = new ZipArchive();
$zip->open($filefoldername.'/xp.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE);
// Create recursive directory iterator
/** #var SplFileInfo[] $files */
$filesZ = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($rootPath),
RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($filesZ as $nameZ => $fileZ)
{
// Skip directories (they would be added automatically)
if (!$fileZ->isDir())
{
// Get real and relative path for current file
$filePath = $fileZ->getRealPath();
$relativePath = substr($filePath, strlen($rootPath) + 1);
// Add current file to archive
$zip->addFile($filePath, $relativePath);
}
}
// Zip archive will be created only after closing object
$zip->close();
I'm guessing that windows over looks something that dos doesn't. zip files made with winRAR are ok, but the ones generated by this code are strange.
I want to generate the .zip in php not by shell command. Can anyone help me figure this out?
Maybe js-dos cannot automatically create intermediate directories, you could try the code below to add intermediate directories to zip file.
$rootPath = realpath($filefoldername."/");
$zip = new ZipArchive();
$zip->open($filefoldername.'/xp.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE);
// Create recursive directory iterator
/** #var SplFileInfo[] $files */
$filesZ = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($rootPath),
// !!!! replace LEAVES_ONLY with SELF_FIRST to include intermediate directories
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($filesZ as $nameZ => $fileZ)
{
// Get real and relative path for current file
$filePath = $fileZ->getRealPath();
$relativePath = substr($filePath, strlen($rootPath) + 1);
$relativePath = str_replace('\\', '/', $relativePath);
if ($fileZ->isDir()) {
$zip->addEmptyDir($relativePath);
} else {
$zip->addFile($filePath, $relativePath);
}
}
// Zip archive will be created only after closing object
$zip->close();
I have a folder with subfolders containing all kind of files..
When I trigger action to delete them, some of them got deleted but some of them don't. What I am trying to say is that they are all pretty much same structure.. I don't know what is causing that? Is it something with timing or? I am deleting the folder with files once the zip files are created.
This is the method which is deleting folder with subfolders and files..
private function removeFiles($name)
{
// Locate folder to remove
$rootDirElements = [
$this->kernel->getProjectDir(),
$name
];
$rootDir = implode(DIRECTORY_SEPARATOR, $rootDirElements);
$rdi = new RecursiveDirectoryIterator($rootDir, RecursiveDirectoryIterator::SKIP_DOTS);
$files = new RecursiveIteratorIterator(
$rdi,
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($files as $file) {
if ($file->isDir()) {
rmdir($file->getRealPath());
} else {
unlink($file->getRealPath());
}
}
rmdir($rootDir);
}
And I am calling it in another method where zip logic is happening:
private function zipFolder($source, $destination, $name, $includeDir = false)
{
$zip = new ZipArchive();
if ($zip->open($destination, ZipArchive::CREATE | ZipArchive::OVERWRITE) === true) {
//zipping logic
}
}
}
}
$zip->close();
// After zip file is created delete folder
$this->removeFiles($name);
I don't know what is causing that?
I'm writing a PHP script that archives a selected directory and all its sub-folders. The code works fine, however, I'm running into a small problem with the structure of my archived file.
Imagine the script is located in var/app/current/example/two/ and that it wants to backup everything plus its sub directories starting at var/app/current
When I run the script it creates an archive with the following structure:
/var/app/current/index.html
/var/app/current/assets/test.css
/var/app/current/example/file.php
/var/app/current/example/two/script.php
Now I was wondering how:
a) How can I remove the /var/app/current/ folders so that the root directory of the archive starts beyond the folder current, creating the following structure:
index.html
assets/test.css
example/file.php
example/two/script.php
b) Why & how can I get rid of the "/" before the folder var?
//Create ZIP file
$zip = new ZipArchive();
$tmpzip = realpath(dirname(__FILE__))."/".substr(md5(TIME_NOW), 0, 10).random_str(54).".zip";
//If ZIP failed
if($zip->open($tmpzip,ZIPARCHIVE::CREATE)!== TRUE)
{
$status = "0";
}
else
{
//Fetch all files from directory
$basepath = getcwd(); // var/app/current/example/two
$basepath = str_replace("/example/two", "", $basepath); // var/app/current
$dir = new RecursiveDirectoryIterator($basepath);
//Loop through each file
foreach(new RecursiveIteratorIterator($dir) as $files => $file)
{
if(($file->getBasename() !== ".") && ($file->getBasename() !== ".."))
{
$zip->addFile(realpath($file), $file);
}
}
$zip->close();
You should try with:
$zip->addFile(realpath($file), str_replace("/var/app/current/","",$file));
I've never used the ZipArchive class before but with most archiver application it works if you change the directory and use relative path.
So you can try to use chdir to the folder you want to zip up.
I am trying to create a web app using codeigniter which will be used over a home or office network. Now Im looking for a backup option which can be done from the web protal. For example, in my htdocs folder i have: App1, App2 etc.
i want to backup and download the App1 folder directly from the webapp which can be done from any client machine which is connected to the server. is it possible. if yes then can you please let me know how?
~muttalebm
sorry for the late reply. I found a quite easy and simple backup option builtin with codeigniter. Hope this helps someone
$this->load->library('zip');
$path='C:\\xampp\\htdocs\\CodeIgniter\\';
$this->zip->read_dir($path);
$this->zip->download('my_backup.zip');
i used the code directly from the view and then just called it using the controller.
~muttalebm
Basically what you want to do is zip the application folder and download it, fairly simple to do. Please check out:
Download multiple files as a zip folder using php
On how to zip a folder for download.
I you do not have that extension a simple command can be used instead, I assume you are running on Linux if not replace command with zip/rar Windows equivalent:
$application_path = 'your full path to app folder without trailing slash';
exec('tar -pczf backup.tar.gz ' . $application_path . '/*');
header('Content-Type: application/tar');
readfile('backup.tar.gz');
Note: Make every effort to protect this file from being accessed by unauthorized users otherwise a malicious user will have a copy of your site code including config details.
// to intialize the path split the real path by dot .
public function init_path($string){
$array_path = explode('.', $string);
$realpath = '';
foreach ($array_path as $p)
{
$realpath .= $p;
$realpath .= '/';
}
return $realpath;
}
// backup files function
public function archive_folder($source = '' , $zip_name ='' , $save_dir = '' , $download = false){
// Get real path for our folder
$name = 'jpl';
if($zip_name == '')
{
$zip_name = $name."___(".date('H-i-s')."_".date('d-m-Y').")__".rand(1,11111111).".zip";
}
$realpath = $this->init_path($source);
if($save_dir != '')
{
$save_dir = $this->init_path($save_dir);
}else{
if (!is_dir('archives/'))
mkdir('archives/', 0777);
$save_dir = $this->init_path('archives');
}
$rootPath = realpath( $realpath);
// echo $rootPath;
// return;
// Initialize archive object
$zip = new ZipArchive();
$zip->open($save_dir . '\\' . $zip_name, ZipArchive::CREATE | ZipArchive::OVERWRITE);
// Create recursive directory iterator
/** #var SplFileInfo[] $files */
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($rootPath),
RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($files as $name => $file)
{
// Skip directories (they would be added automatically)
if (!$file->isDir())
{
// Get real and relative path for current file
$filePath = $file->getRealPath();
$relativePath = substr($filePath, strlen($rootPath) + 1);
// Add current file to archive
$zip->addFile($filePath, $relativePath);
}
}
// Zip archive will be created only after closing object
$zip->close();
if($download){
$this->download($zip);
}
}
Problem
I am building an online file manager, for downloading a whole directory structure I am generating a zip file of all subdirectories and files (recursively), therefore I use the RecursiveDirectoryIterator.
It all works well, but empty directories are not in the generated zip file, although the dir is handled correctly. This is what i am currently using:
<?php
$dirlist = new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS);
$filelist = new RecursiveIteratorIterator($dirlist, RecursiveIteratorIterator::SELF_FIRST);
$zip = new ZipArchive();
if ($zip->open($tmpName, ZipArchive::CREATE) !== TRUE) {
die();
}
foreach ($filelist as $key=>$value) {
$result = false;
if (is_dir($key)) {
$result = $zip->addEmptyDir($key);
//this message is correctly generated!
DeWorx_Logger::debug('added dir '.$key .'('.$this->clearRelativePath($key).')');
}
else {
$result = $zip->addFile($key, $key);
}
}
$zip->close();
If I ommit the FilesystemIterator::SKIP_DOTS I end up having a . file in all directories.
Conclusion
The iterator works, the addEmptyDir call gets executed (the result is checked too!) correctly, creating a zip file with various zip tools works with empty directories as intendet.
Is this a bug in phps ZipArchive (php.net lib or am I missing something? I don't want to end up creating dummy files just to keep the directory structure intact.