Our application overwrites an already existing ZIP file.
The zip file is downloadable, for ZIP creation we use PHP ZipArchive.
Now the question:
Do I have to create a TMP ZIP file and rename it to the desired downloadable filename to have an atomic operation and all time access to a valid ZIP file, or is the ZIP creation process of ZipArchive already atomic?
Edit:
After testing it with multiple addFile calls, separated with a sleep, it seems like that the file is created / overwritten on the final close call. But the question remains, is the final ZIP file creation atomic?
After testing it with a huge directory structure (the zip creation needed ~ 10seconds), and a "watch -n 0.1 unzip -t foo.zip" on the resulting ZIP file, it seems like that the ZIP file creation is atomic.
At least I got no errors while testing the ZIP file.
Related
I have a zip file which contains around 1058 tgz files. In PHP, I extract the tgz files from the zip file one by one and process and then decompress the tgz file and process the content. The decompression works fine till 1016th tgz file. From 1017th tgz files I am getting "unable to create temporary file" error.
I am usisng PharData->decompress() method to decompress the tgz file in which line I am getting this error from 1017th file.
I have tried with another zip file which has 1158 files. In this case also, only 1016 files were processed (i.e., decompress and other functionalities worked fine). From 1017th file the decompress method throws the above error.
This is a bug #58169, which is on my list to remediate in the coming months. Based on my initial triage, the problem occurs because of the way the Phar library holds open temporary files during (de)compression. There is no way to get around it in PHP. However, you can mitigate it at the OS level.
The number after which the error occurs varies based on your operating system and the imposed open file limit. Web search for "raise open file limit" and your OS to find specific instructions. On Linux, for example, you might do something like is mentioned in this SO answer:
try ulimit -n 2048 and if that fails
modify the values in /etc/sysctl.conf.
I'm trying to see if a directory exists inside a ziparchive using statName and I have come across an odd quirk.
When I generate a zip file in php I add the files in this fashion: $this->zip->addFile($file, $path); where $file is C:\xampp\htdocs\images\uploads\about.png and $path is the internal path images/uploads/about.png. If I try and see if images/uploads/ exists when checking the zip in another routine using statName I get a false response even though the files and directories exist if opened by winzip.
For testing I made a new zip using winzip, and added a folder images and in and then uploads mimicking the above and checked if images/uploads/ existed using statName and it does!
I've made an output of the zip file using a loop and you can clearly see in the ziparchive created zip folder there is no verbatim images/uploads/ directory, yet there are files in those directories if you open the zip and as seen in output. Whereas the winzip one has the directory listed verbatim.
What is preventing the statName from showing up with the ZipArchive created zip file? I can only assume its because the dir is not listed verbatim, but that doesn't make any sense as those directories do exist! Is there something I don't know in the way ZipArchive handles adding files?
We would like to protect our zip files by registering the user into the installer at download time.
Is there any place in a zipped package where a script could add such hidden info?
The note field is not good as most zip software displays it.
A zip archiver will read a zip file from the end and find entries using offsets. If you put bytes at the beginning of the zip file (and make sure the offsets are still correct !), you will get a valid zip file and I doubt archivers will show the additional content. That's how self-extracting zip files work: the file starts with the unzip exe and ends with the actual zip content.
In your case, you could prepare archives with a fixed-length blob at the beginning and overwrite it when a user download it.
Note that won't "protect" the zip file, just mark the zip file as downloaded by a given user (and the mark can be removed easily).
I wish to zip a directory on the server and download it to the user. I've found great information on this site on how to zip the directory.
The part that has me stumped is where to store the temporary file. From what I've read, zipArchive requires that the zip file be saved on the server, and I can't download it without saving it. Correct?
I've seen solutions where they recommend saving it as "somename.zip", and then using readfile() along with the appropriate headers to download, and then using unlink() after the download to get rid of it. But where to store "somename.zip" and what if there are collisions?
So, then I started thinking that I should create a file with a random name in some temporary directory. Or maybe I should use something like tmpfile(), but that returns a file handler and I don't know how that will work with zipArchive.
For my application, the size of the directory is fairly small, and would like to not even write it to disk, but to memory instead. Maybe something like $tmp_handle = fopen('php://temp', 'r+');, but again this returns a file handler and I don't know how that will work with zipArchive.
How should the zip file created by zipArchive be temporary saved?
As ZipArchive class works with stacked streams, the $filename parameter cannot be php://temp or php://memory. It has to be a 'real' filepath on the filesystem.
Some lines of thought :
As said in a comment, tempnam() function can help you to avoid temporary files names collisions. I think storing the file temporarily and readfile()'ing it is the most common and not a bad option
If you absolutely want that temporary file be stored in memory, you can mount a tmpfs directory on your Linux system. It will be seen as a 'normal' directory for PHP but will be stored in RAM
If you can use shell_exec() function, you can zip the directory in a command line and read the output in PHP : it will be stored in memory.
In my opinion, the first option (your first idea) is better in most cases, because :
allowing the use of shell_exec() can be dangerous
if one day you have big files to zip, this could fill the memory, resulting in very poor performance
In a single script, I need to do the following:
create a zip out of files in a directory
force the download of the newly create zip
My problem is that whenever I try to do this in a single script,
the downloaded zip is corrupted (filesize is ok though). If I trig the
processes in two separate script calls, the downloaded zip is ok.
I guess that the problem is that the zip saving to file process isn't completely
finished before the download starts. Strangely, it doesn't solve the problem to
insert a sleep(3) between the processes... Code outline below.
How to assure that the zip file is completely finished before the force download starts?
Regards / Jonas
// 1. create a zip
$createZipFile = new CreateZipFile('temp.zip');
$createZipFile->zipDirectory('temp/', '.');
$createZipFile->saveZipFile();
sleep(3); // <-- Doesn't matter!
// 2. force zip download
$fileServer = new FileServer();
// Line below gives a corrupted zip when run in same script as 1.
$fileServer->forzeDownload('temp.zip');
Create the zip and then redirect the user to that file.
http://php.net/manual/en/function.header.php
Thank you, JorenB and Gumbo!
The texteditor examination revealed some debug output in zip creation that doesn't affect the orignial zip, but corrupts the data sent to the browser when downloading.