Got a bit of a weird problem.
I have a folder with PowerPoint data that I need to zip and then rename to pptx.
If I manually create a .zip and copy the files into it, then rename, it works like expected, but when I use ZipArchive to archive the data and then rename the created file, it doesn't work.
The programmatically created archive is also a few kB smaller than the manually created one.
Archive comparison tools tell me that I've got the exact same files in both zips, including hidden files.
But here is where it gets really weird: if I make another, empty archive, then copy-paste the files from the programmatically created archive with a simple select, drag and drop, that new archive will work when renamed to a .pptx and has the same size as the first manually created file.
$dir = 'pptx/test/compiled';
$zip_file = 'pptx/test/file.zip';
// Get real path for our folder
$rootPath = realpath($dir);
// Initialize archive object
$zip = new \ZipArchive();
$zip->open($zip_file, \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();
update
This only seems to be a problem on Windows, when tested on a Mac, it works without issues.
I ran into this problem again, and after asking help on a LibreOffice forum (the problem was specific for Impress), I've discovered that in windows the filepaths used when archiving had backslashes, that caused the issues.
I've added two lines in my archiving function to clean up the paths:
// Get real and relative path for current file
$filePath = $file->getRealPath();
$relativePath = substr($filePath, strlen($rootPath) + 1);
// Replace backward with forward slashes, for compatibility issues
$filePath = str_replace('\\', '/', $filePath);
$relativePath = str_replace('\\', '/', $relativePath);
// Add current file to archive
$zip->addFile($filePath, $relativePath);
Now my code runs without problems. (I use the LibreOffice headless as an exec() to convert to pdf, which wouldn't work previously)
Related
So our code creates a zip file and inside it are PDF's from a specific folder. Everything works fine, except that when this zip file is downloaded to a computer and the files are extracted - the modification time for each PDF file follows the server's time zone. (Our site is on a GoDaddy shared server, and they're located in Arizona. We are located in Singapore.)
Is there a way to modify this? I have tried to set the default time zone or use touch() but no luck.
// Initialize archive object
$zip = new ZipArchive();
$res = $zip->open(plugin_dir_path( __FILE__ ).'MortgageWise-All-Reports.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE);
if ($res === TRUE) {
// Create recursive directory iterator
/** #var SplFileInfo[] $files */
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($rootPath)
);
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));
date_default_timezone_set("Asia/Singapore");
//touch($filePath, );
// Add current file to archive
$zip->addFile($filePath, $relativePath);
}
}
// Zip archive will be created only after closing object
$zip->close();
}
else {
echo "File not created.";
}
I would start by trying to set the timezone at the start of the script, before you create the ZIP, rather than in the foreach loop.
But with that being said I don't believe it will make much difference as the PDF files already exist? And therefore will already have those properties assigned to them!
Do you have access to Web Host Manager or similar? If you have a VPS or Dedicated server, you can change the time zone of the server yourself.
EDIT: sorry, you already stated you are on a shared server, would highly recommend a VPS to give you full control over these settings, should only be $50 a month
What i would like to do, is to zip my fileadmin and my extension folder with a zipAction on my controller. So i took a piece of code from here
and i have this:
$fileAdminPath = 'fileadmin';
$archiveName = $importerSettings['fileadminArchiveName'];
$zip = new \ZipArchive;
$zip->open($archiveName, \ZipArchive::CREATE | \ZipArchive::OVERWRITE);
$files = new \RecursiveIteratorIterator (new \RecursiveDirectoryIterator($fileAdminPath), \RecursiveIteratorIterator::LEAVES_ONLY);
if (!$file->isDir()) {
// Get real and relative path for current file
$filePath = $file->getRealPath();
$relativePath = substr($filePath, strlen($fileAdminPath) + 1);
// Add current file to archive
$zip->addFile($filePath, $relativePath);
}
// Zip archive will be created only after closing object
$zip->close();
Now, when the code reaches the isDir() part, i get the following error:
Call to a member function isDir() on null
If i remove it and let the code run again, the getRealPath() gets the same error. So i am guessing i missing a namespace or something like that, that stores these functions.
On a local php file (index.php) and MAMPP it works. On TYPO3, not.
By the way, i am not sure if the controller really reads the fileadmin path. If i got it wrong, please feel free to correct me.
Best regards
If you run your script inside an extension, this script is searching fileadmin within your extension directory.
Try to use the following script:
$fileAdminPath = \TYPO3\CMS\Core\Utility\GeneralUtility::getFileAbsFileName('fileadmin');
Edit:
You should also walk the files in an foreach because you scan an directory recursiv:
foreach ($files as $file) {
if (!$file->isDir()) {
// Get real and relative path for current file
$filePath = $file->getRealPath();
$relativePath = substr($filePath, strlen($fileAdminPath) + 1);
// Add current file to archive
$zip->addFile($filePath, $relativePath);
}
}
When I make ZIP archive using PHP ZipArchive object, all files inside the archive have permissions set to 666, although original files have permissions set to 644.
My script makes the zip archives properly, just the permissions are messed up.
////// Make Template archive object
$templateArchive = new ZipArchive();
$templateArchive->open(PATH_TO_TEMPLATES.'_files/'.$templateName.'/_pack/'.$templateName.'.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE);
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($templateDir."/templates/".$template_archive_name),
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();
// relative path is full path, reduced with length of templateDir string and 11 more chars for /templates/
$relativePath = substr($filePath, strlen($templateDir) + 11);
// Add current file to archive
$templateArchive->addFile($filePath, $relativePath);
}
}
// Template Zip archive will be created only after closing object
$templateArchive->close();
P.S. I am working of MAMP on Mac. I just found out that issue is only when PHP 5.6.10 version is selected. When I select 5.5.26, the permissions of files are correct.
You can preserve the Unix permissions in the files you archive by adding the following code after your ZipArchive::addFile() statement:
$templateArchive->setExternalAttributesName($relativePath,
ZipArchive::OPSYS_UNIX,
fileperms($filePath) << 16);
This updates the external attributes of the entry at $filePath with the actual file's permissions.
Unix permissions can be stored in ZIP files in each entry's external attribute, but you have to shift 16 bits to store the permissions in the right place in the external attribute, which is why << 16 is applied to the output of fileperms($filePath).
The documentation of ZipArchive::setExternalAttributesName() also has an example that features Unix permission setting: https://secure.php.net/manual/en/ziparchive.setexternalattributesname.php
As for the permissions of the directories, you'll need to add the directories with ZipArchive::addEmptyDir() (because ZipArchive::addFile() on a directory will result in an error) and then apply the permissions with ZipArchive::setExternalAttributesName() the same way you did for the regular files.
Due to the way that RecursiveDirectoryIterator works, the current directory name will end with /., and due to the way that ZipArchive::addEmptyDir() works, stored directories will end with /. To apply ZipArchive::setExternalAttributesName(), you have to provide the exact entry name, so I suggest lopping off the trailing dot for both methods.
Here is your code edited to support the preservation of permissions of files and directories:
////// Make Template archive object
$templateArchive = new ZipArchive();
$templateArchive->open(PATH_TO_TEMPLATES.'_files/'.$templateName.'/_pack/'.$templateName.'.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE);
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($templateDir."/templates/".$template_archive_name),
RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($files as $name => $file)
{
// Get real and relative path for current file
$filePath = $file->getRealPath();
// relative path is full path, reduced with length of templateDir string and 11 more chars for /templates/
$relativePath = substr($filePath, strlen($templateDir) + 11);
// Add regular files
if (!$file->isDir())
{
// Add current file to archive
$templateArchive->addFile($filePath, $relativePath);
}
elseif (substr($relativePath, -2) === "/.")
{
// Remove the dot representing the current directory
$relativePath = substr($relativePath, 0, -1);
// Add current directory to archive
$templateArchive->addEmptyDir($relativePath);
}
else
{
continue;
}
$templateArchive->setExternalAttributesName($relativePath,
ZipArchive::OPSYS_UNIX,
fileperms($filePath) << 16);
}
// Template Zip archive will be created only after closing object
$templateArchive->close();
My project consist in a big mongodb/gridfs files database, where each file belongs to a "folder" document, and these folders are ordered following a tree structure with one root node.
Now I need to zip a part of the tree (or maybe the whole structure), keeping the tree and files accordingly (the whole tree will be approximately 10To big).
What's the best strategy to do so ? All files are dynamically provided by gridFS (the cache musnt be used in the zip process).
What's the best way to do so ? Thanks for your help !
See answer by Dador https://stackoverflow.com/questions/4914750/how-to-zip-a-whole-folder-using-php
// Get real path for our folder
$rootPath = realpath('folder-to-zip');
// Initialize archive object
$zip = new ZipArchive();
$zip->open('file.zip', 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();
I'm trying to make zip file from a folder inside my localhost server which is my computer with windows 7 OS to a host .The problem is with using getRealPath for making the zip file the zip file content would be like this "D:\xampp\htdocs\mobl\admin\downloads" but I don't want those folders inside my zip file.
How should I make zip file without those folders and just the target folder ?
this is the code I'm using to zip the folder
enter code here
$zip = new ZipArchive;
$zip->open('downloads/'.$zipname,ZipArchive::CREATE);
// Initialize empty "delete list"
$filesToDelete = array();
// Create recursive directory iterator
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($rootPath),
RecursiveIteratorIterator::LEAVES_ONLY);
foreach($files as $name => $file) {
// Get real path for current file
$filePath = $file->getRealPath();
// Add current file to archive
$zip->addFile($filePath);
}
// Zip archive will be created only after closing object
$zip->close();
Thanks