Create a Zip File Using PHP
Creating .ZIP archives using PHP can be just as simple as creating them on your desktop. PHP's ZIP class provides all the functionality you need! To make the process a bit faster for you, I've coded a simple create_zip function for you to use on your projects.
The PHP
/* creates a compressed zip file */
function create_zip($files = array(),$destination = '',$overwrite = false) {
//if the zip file already exists and overwrite is false, return false
if(file_exists($destination) && !$overwrite) { return false; }
//vars
$valid_files = array();
//if files were passed in...
if(is_array($files)) {
//cycle through each file
foreach($files as $file) {
//make sure the file exists
if(file_exists($file)) {
$valid_files[] = $file;
}
}
}
//if we have good files...
if(count($valid_files)) {
//create the archive
$zip = new ZipArchive();
if($zip->open($destination,$overwrite ? ZIPARCHIVE::OVERWRITE : ZIPARCHIVE::CREATE) !== true) {
return false;
}
//add the files
foreach($valid_files as $file) {
$zip->addFile($file,$file);
}
//debug
//echo 'The zip archive contains ',$zip->numFiles,' files with a status of ',$zip->status;
//close the zip -- done!
$zip->close();
//check to make sure the file exists
return file_exists($destination);
}
else
{
return false;
}
}
Sample Usage
$files_to_zip = array( 'preload-images/1.jpg', 'preload-images/2.jpg', 'preload-images/5.jpg', 'kwicks/ringo.gif', 'rod.jpg', 'reddit.gif' ); //if true, good; if false, zip creation failed $result = create_zip($files_to_zip,'my-archive.zip');
The function accepts an array of files, the name of the destination files, and whether or not you'd like the destination file to be overwritten if a file of the same name exists. The function returns true if the file was created, false if the process runs into any problems.
This functionality is great for web-based file managers. Who knows, I may just submit this to Christoph's MooTools FileManager project. Happy zipping!
Discussion
Be Heard!
Share your thoughts with fellow developers of all skill levels! I want to hear from you!
Hi, I have a question. I looked everywhere, the doc on php.net, google, forums, but I can’t find out: How do you set the compression method (storage, fast, normal, best) with the ZipArchive class? I need to set it to the storage mode, so it just groups files together without compressing them, because those files are already compressed (jpeg, mp3) and the compression actually take some times with big files like mp3s. I do use caching (I just don’t delete the archive after submission), but the thing is the archives need to be created at least once, and it takes forever. If anyone knows, I’m interested
Everyday I check my feeds, and every day you seem to have written another ridiculously informative article! Thanks again, and where do you find the time! :)
@Luke: I have a very patient girlfriend… :)
How about compressing an entire folder?
@ahmed: you’d have to modify the script so it checks for each file if it’s a folder with the is_dir function, then use open_dir to get all the files and add them to the array. Just make sure you test for “.” and “..” when looping through the folder so they are not added to the array. Oh and check for sub folders with is_dir too. A recursive function would be ideal. Then i think there is function to create folders inside zip archive.
So it would definitely need a little bit of work, but it can be done. I recommend you look at the doc of is_dir, open_dir and ZipArchive::addEmptyDir on php.net. There are always so nice user posted example for each function
I’ve had problems in the past where a client’s webhost doesn’t have the zlib package installed in their php. I actually worked up a work-around utilizing the zip and unzip programs built into *nix systems. Funny though, I couldn’t test/debug locally on my windows machine without adding a path to the system, and so I just kept uploading the script and testing anyways. I are silly sometimes. =P
You can put “array” before the first parameter, this will make sure only an array can be passed into it. You can only do this for arrays and classes iirc. If I pass “hello” as the first parameter, the script will break. count(‘hello’) will return (int) 1 which, when casted to a bool be TRUE. Using the modified code above will result in the following error while passing a string value.
You sir, are a god.
Maybe we can create rar archives via exec commands?
That’s a good question! Lemme know here if you come up with any answers. ;D/
HI,
i got error on line $zip = new ZipArchive(); . I don’t know why although i had enable the PHP_Zip module. Can anybody tell why.
Yah I ran into the same problem as saqib on my two servers that I have zlib 1.2.3 installed on:
Fatal error: Class ‘ZipArchive’ not found
Just another great article from David.
Oh and having a “patient girlfriend” is a gift
Some very nice, informative articles here, stuff in PHP I can use for Coldfusion as well. But this one is even simpler in CF:
;-)
Thanx, and I’ll follow your writings via the RSS-feed!
Darn, tags are filtered out, so I’ll just write it in plain English: <cfzip>
Hi David! Great function. Saved me some time and some hair!
I was using it to ZIP files that weren’t in the same directory as the script, and so I was getting lots of nested directories. Normally this is a Good Thing, but I’m using the script in a situation where I’m only zipping one file at a time, so I wanted to get rid of the subdirectories. I added a fourth parameter to the function: $distill_subdirectories = true, and then changed the code around line 24 to:
//add the files
foreach($valid_files as $file) {
if ($distill_subdirectories) {
$zip->addFile($file, basename($file) );
} else {
$zip->addFile($file, $file);
}
}
Figured that might be helpful to some.
(Note that the _ should be _)
Is it possible to change the destination directory of the files in the ZIP? I don’t want my files to inherit the same directory structure as their original path.
hi,
I’ve used code very similar to this expect I get no zip file actually made
after creating the file, and adding a file to it I have the following code:
echo “numfiles: ” . $zip->numFiles . “”;
echo “status:” . $zip->status . “”;
if ( $zip->close()) {echo “Success!”;}
else { echo ” failed to save “;}
numfiles gives me a result of 1
stsua gives 0
and I get “failed to save”
I’m fairly sure this is not a permissions issue since another file in the same directory opens several files then creates a new one from excerpts. I’ve been searching google all day but can find to reference to this anywhere ( ‘cept one post on php.net which states its a permissions issue)
Thanks in advance,
Verbosity
Nice zip class tutorial:) When zipping up multiple files or whole directories, how do you control zips that will potentially use up all the server’s memory? ie. trying to make a zip file from 2GB of data.
@Jeff: You wouldn’t want to attempt that, I don’t think.
Thanks for the speedy response. Yea, that’s the same answer I came up with. I guess a quick and dirty solution could be to calculate the potential zip content size on page load and disable the ability to zip the whole folder if the content size is too great. Just curious if you’d ever run into this problem.
I know when a website creates even a 100mb zip there is a very noticeable wait for the page to load…
Hi David, and thanks a lot for what you do, and the way you do it !
I would like to adapt your script to use it on my website. But I would like to get the file names to zip from records in mysql database ?
How can I do that ?
Hope you can give me an hint ! ;)
The plugin you use to convert you code into the snazzy display above, also converts & to & when you select “Raw Code”. Wich means anyone copying the “Raw Code” will get errors. Thought you might wanna know.
:)
Adam
Thanks broo….
Its very usefull, u sample code is working and no error.
Best Regards,
Adin
Your script worked perfectly for me. Really happy with that!
Thanks,
Mono
this is clear for last else-if:
function create_zip($files = array(),$destination = ”,$overwrite = false)
{
if(file_exists($destination) && !$overwrite)
return false;
$valid_files = array();
if(is_array($files))
foreach($files as $file)
if(file_exists($file))
$valid_files[] = $file;
if(empty($valid_files))
return false;
$zip = new ZipArchive();
if($zip->open($destination,$overwrite ? ZIPARCHIVE::OVERWRITE : ZIPARCHIVE::CREATE) !== true)
return false;
foreach($valid_files as $file)
$zip->addFile($file,$file);
//debug
//echo ‘The zip archive contains ‘,$zip->numFiles,’ files with a status of ‘,$zip->status;
$zip->close();
return file_exists($destination);
}
Hello David,
This is a really nice snippet. However, I was trying the following scenario
1. passing the complete physical path of two diff files placed in two diff locations.
2. trying to create a zip file from these two files at a seperate location [passing the complete physical path to the 'destination' parameter as well]
This , however, is not creating the zip file as desired. Anything to do with the paths ?
You have posted really tremendous work. Thanks.
The result .zip file successfull open with winrar, but if we use 7zip or winzip it will be show error for extract the file zip.
I write a recurse function which can zip a file or a complete directory. This function only require the path of the file or directory as argument. You can read this function here:
http://ramui.com/articles/php-zip-files-and-directory.html
I have used your test example above. The zip file appears to have been created and they filesize suggests there are contents, but on opening the zip file (windows platform), it is empty?
Any ideas?
@Jam: I second this observation. Having now worked out how to create the zip file on a windows platform (although not accessible using XPs zip), the file is added mimicking the folder structure from where it was added. I only need the actual file i want to attach, not all parent folders aswell.
Hi David,
Can we make a functions for choose file name from table, move them in a location and then add all that in a archive file.
Hi Mr Lilhare,
I have coded this, and maybe it can help you.
First I display a list of songs picked form a database in a table, with checkboxes.
When the user have finished picking his songs, He clicks on a button, and then magic happens. The script zip the files chose in a archive. I didn’t see any interest in moving the songs to another place to create thz zip, but it is easy to code. In my case, when the zip is ready, I copy it into the user private directory, and I even add a PDF with the song list into the zip.
//$files_to_zip=array();
$id_utilisateur = $_GET['id_utilisateur'];
$id_synchro = $_GET['mb'];
//$fichier_pdf = “$id_utilisateur/AUTORISATION-$id_synchro.pdf”;
mysql_select_db($xxxxx, $xxxxx);
$query = mysql_query(“SELECT DISTINCT table_synchro.id_synchro, table_musique.nom_fichier FROM (table_musique LEFT JOIN table_synchro ON table_synchro.id_musique=table_musique.id_musique) WHERE table_synchro.id_synchro=’$id_synchro’ “) or die(mysql_error());
while ($row = mysql_fetch_assoc($query)) {$files_to_zip[] = ‘../high/’.$row['nom_fichier'].’.mp3′;}
$files_to_zip[] = $id_utilisateur.’/AUTORISATION-’.$id_synchro.’.pdf’;
//print_r($files_to_zip);
$chemin= “fiche.php?id_synchro=$id_synchro&KT_back=1&new=1″;
header(“Location: $chemin”);
/* creates a compressed zip file */
function create_zip($files = array(),$destination = ”,$overwrite = false) {
//if the zip file already exists and overwrite is false, return false
if(file_exists($destination) && !$overwrite) { return false; }
//vars
$valid_files = array();
//if files were passed in…
if(is_array($files)) {
//cycle through each file
foreach($files as $file) {
//make sure the file exists
if(file_exists($file)) {
$valid_files[] = $file;
}
}
}
//if we have good files…
if(count($valid_files)) {
//create the archive
$zip = new ZipArchive();
if($zip->open($destination,$overwrite ? ZIPARCHIVE::OVERWRITE : ZIPARCHIVE::CREATE) !== true) {
return false;
}
//add the files
foreach($valid_files as $file) {
$zip->addFile($file,basename($file));
}
//debug
//echo ‘The zip archive contains ‘,$zip->numFiles,’ files with a status of ‘,$zip->status;
//close the zip — done!
$zip->close();
//check to make sure the file exists
return file_exists($destination);
}
else
{
return false;
}
}
$nom_zip = ‘Name-’.$id_synchro.’.zip’;
$nouveau_nom_zip = $id_utilisateur.’/’.$nom_zip;
$perdu = $id_utilisateur.’/index.html’;
//if true, good; if false, zip creation failed
$result = create_zip($files_to_zip,$nom_zip);
echo $result;
if (is_dir($id_utilisateur)) {
//chmod($nom_zip,0777);
copy ($nom_zip, $nouveau_nom_zip);
unlink ($nom_zip);
}
else
{
mkdir($id_utilisateur,0777);
//chmod($nom_zip,0777);
copy ($nom_zip, $nouveau_nom_zip);
copy (“perdu.html”, $perdu);
unlink ($nom_zip);
}
mysql_free_result($query);
@Eric Strathmeyer: Good function by David, and i have similar situation so these lines help me as well. Thanks
Compressing an entire folder
function directoryToArray($directory, $recursive) {
$array_items = array();
if ($handle = opendir($directory)) {
while (false !== ($file = readdir($handle))) {
if ($file != “.” && $file != “..” && $file != “Thumbs.db”) {
if (is_dir($directory. “/” . $file)) {
if($recursive) {
$array_items = array_merge($array_items, directoryToArray($directory. “/” . $file, $recursive));
}
$file = $directory . “/” . $file;
$array_items[] = preg_replace(“/\/\//si”, “/”, $file);
} else {
$file = $directory . “/” . $file;
$array_items[] = preg_replace(“/\/\//si”, “/”, $file);
}
}
}
closedir($handle);
}
return $array_items;
}
$files_to_zip = directoryToArray(“myfolder”,true);
thanks for great code ♥♥♥
thanks nitin, works like a charm
Wow mate! Thank you very much! Excactly what I was looking for! Now I need to find a way to backup a complete website in this zip. Then use it as a weekly cron. Any ideas?
i have used that code…
but i stiil get error when i extract the file.zip..
”crc failed….this file is corrupt.”
I used your code and it seemed to work file. However, when I try to ‘extract file’ in windows explorer I get a message the ‘windows has blocked access to these files to help protect your computer’. It suggests that I unblock the file in the properties dialog, but no such option exists.
So, is windows not capable of extracting the files or is it a corrupt zip file? Anybody have experience with this?
Thanks.
I’m getting this same behavior. Work on Ubuntu archive mounter, but not in archive manager. Doesn’t work on windows.
Here’s the error message on linux:
7-Zip 9.04 beta Copyright (c) 1999-2009 Igor Pavlov 2009-05-30
p7zip Version 9.04 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,4 CPUs)
Error: /home/michael/Downloads/dsat-assessment.zip: Can not open file as archive
Errors: 1
Any suggestions?
Hey David and Anybody Here,
I used the david snippet in my local machine, it doesn’t work as it doesn’t get the files I have written in array,
do I have to write the complete path..? pls provide some example..
@nitin:
Hello Nitin, I have used ur code .I am not getting any error, but zip file is also not created.Anything else is needed.
U r help is needed.
Thanks,
Sheetal
@sheetalmhalsekar
hi sheetal, function posted by me creates an array list of directory items.
$files_to_zip = directoryToArray(“myfolder”,true);
now pass the array “$files_to_zip” to create_zip function
$result = create_zip($files_to_zip,’my-archive.zip’);
I’m having problems implementing the script in the SilverStripe CMS. I added a few else statements after some of the ifs that don’t have them so I could see where the problem is. It seems to be throwing errors when in cycles through each of the files. It says they don’t exist. Any ideas?
Hi David
Thanks for this script – it’s a lifesaver!
Question – I”ve used this function as part of a web application that creates downloadable archives of files on the fly for my users. However – when they download the zips onto a PC (running XP) the PC won’t open them – saying that the files are untrusted because they have come from another computer.
Is there anything I can add to my zip-archives at the point of creation which will stop them from getting blocked by SP2 filters?
I hate PCs…
how to create zip from directory?
thanks
For all those here who have had an issue with nested folders inside your zip file with PC/Windows blocking extraction – I too had this issue. eg I have modified this script to keep the nested folder structure by passing in an array of files to add with source & destination keys.
When my windows users (I’m a mac man myself) couldn’t open I started to wonder whether it was an encoding error as some of my folder names contain parentheses – not by my design but a default of Adobe’s where they export artwork files to a (Footage) folder. But I digress.
The issue ended up being a dirty old slash / at the beginning of my nested folder structure. eg /(Footage)/foo/bar After removing proceeding / eg (Footage)/foo/bar – all worked fine.
Note that programs like winzip handled the opening / slash fine – just the builtin windows extractor had the issue.
I hope this helps out any if at all – took me a few hours to sort.
Best
Kyle
@daniel (or anyone else getting an “empty” zip file)
I too was getting zip files of the appropriate size, but appeared empty when viewed and and I tried to extract, windows was telling me the file was invalid. After looking at the ZipArchive manual on php.net, the problem stems from:
$zip->addFile($file,$file);unless you have the function in the same directory as the files you want to zip, you will need to include the file path (which most people will if they use the function as an include). The 2nd parameter in addFile is the name of the file inside the zip, so if your $file var includes the path, that’s where the nested folder issue is coming from. So to solve this, I changed the code to :
$just_name = preg_replace("/(.*)\/?([^\/]+)/","$2",$file);
$zip->addFile($file,$just_name);
which will strip out the file path (if any) and leave you only the file name for the 2nd variable in addFile. Now my zips open and extract just fine = )