Creating ZIP files with PHP
UPDATE: You are better off using the library mentioned in this post.
As part of my previously mentioned OpenVPN CA I want to deliver keys, certs and config files to users in a single zip file that they can just extract onto their computers. PHP's own ZIP File Functions only supports reading zip files and not making them.
Some Googling discovered an article by John Coggeshall that can create zip files. It does this by creating the binary data on the fly and can output the zip files directly to the browser from memory or by writing it to disk.
I had some troubles getting hold of a usable version of this code since all these PHP code collection sites have this annoying habit of only showing the syntax highlighted versions of the code rather than give a download link. Eventually got one though and I figured I'll host a mirror of it here to help people out.
Using it is very simple, this is a quick sample that will create a ZIP file and add one directory and one file into then send it directly to the client.
<?
require ("incl/zipfile.inc.php");
$zipfile = new zipfile();
$filedata = implode("", file("incl/zipfile.inc.php"));
$zipfile->add_dir("incl/");
$zipfile->add_file($filedata, "incl/zipfile.inc.php");
header("Content-type: application/octet-stream");
header("Content-disposition: attachment; filename=zipfile.zip");
echo $zipfile->file();
?>


May 24th, 2005 - 14:31
What’s wrong with using gzcompress()/gzdeflate()/gzencode() in the zlib extension?
May 24th, 2005 - 14:33
In the class it uses the gzcompress() function to compress the actual data, but this also puts all the various bits in to make a actual ZIP file as usable by Windows rather than gzip files.
July 26th, 2005 - 13:54
some aprovement .. anyhow .. thx a lot
Hendrik
>> zipfile.inc.php datasec[] = $fr;
$new_offset = strlen(implode(“”, $this->datasec));
// ext. file attributes mirrors MS-DOS directory attr byte, detailed
// at http://support.microsoft.com/support/kb/articles/Q125/0/19.asp
// now add to central record
$cdrec = “\x50\x4b\x01\x02″;
$cdrec .=”\x00\x00″; // version made by
if (strtoupper($type) == “DIRECTORY”) {
$cdrec .=”\x0a\x00″; // version needed to extract
$cdrec .=”\x00\x00″; // gen purpose bit flag
$cdrec .=”\x00\x00″; // compression method
}
if (strtoupper($type) == “FILE”) {
$cdrec .=”\x14\x00″; // version needed to extract
$cdrec .=”\x00\x00″; // gen purpose bit flag
$cdrec .=”\x08\x00″; // compression method
}
$cdrec .=”\x00\x00\x00\x00″; // last mod time & date
$cdrec .= pack(“V”,$crc); // crc32
$cdrec .= pack(“V”,$c_len); // compressed filesize
$cdrec .= pack(“V”,$unc_len); // uncompressed filesize
$cdrec .= pack(“v”, strlen($name) ); // length of filename
$cdrec .= pack(“v”, 0 ); // extra field length
$cdrec .= pack(“v”, 0 ); // file comment length
$cdrec .= pack(“v”, 0 ); // disk number start
$cdrec .= pack(“v”, 0 ); // internal file attributes
if (strtoupper($type) == “DIRECTORY”) {
$cdrec .= pack(“V”, 16 ); // external file attributes – ‘directory’ bit set
}
if (strtoupper($type) == “FILE”) {
$cdrec .= pack(“V”, 32 ); // external file attributes – ‘archive’ bit set
}
$cdrec .= pack(“V”, $this -> old_offset ); // relative offset of local header
// echo “old offset is “.$this->old_offset.”, new offset is $new_offset”;
$this -> old_offset = $new_offset;
$cdrec .= $name;
// optional extra field, file comment goes here
// save to central directory
$this -> ctrl_dir[] = $cdrec;
}
function file() {
// dump out file
$data = implode(“”, $this -> datasec);
$ctrldir = implode(“”, $this -> ctrl_dir);
return
$data.
$ctrldir.
$this -> eof_ctrl_dir.
pack(“v”, sizeof($this -> ctrl_dir)). // total # of entries “on this disk”
pack(“v”, sizeof($this -> ctrl_dir)). // total # of entries overall
pack(“V”, strlen($ctrldir)). // size of central dir
pack(“V”, strlen($data)). // offset to start of central dir
“\x00\x00″; // .zip file comment length
}
}
#——————————————— Hendrik Muus
# require (“zipfile.inc.php”);
#
# $zipfile = new zipfile();
#
# $zipfile->add_entry(“directory”, “testme/”, “”);
# $zipfile->add_entry(“file”, “testme/test.file.bin”, implode(“”, file(“test.file.bin”)));
#
# header(“Content-Type: archive/zip\n\n”); // this gives you a nice open / save dialog ;o)
# header(“Content-disposition: attachment; filename=zipfile.zip”);
#
# echo $zipfile->file();
#———————————————
?>
October 4th, 2005 - 14:22
Hi, can we use this class in commercial products ?
I don’t see any licence info.
October 4th, 2005 - 14:35
Unsure, but like I said on the first line of this posting, its shitty, use the other one, see the link right on top of this post.
October 10th, 2005 - 08:34
Here you can find class + demo + class manual
http://www.smiledsoft.com/demos/phpzip/
for class which you will start using in minutes after installation.
Clear demos explain how to use every function.
A lot of features.
Free version is capable of creating ZIPs only.
Pro is capable to extract files and more.
Please visit
http://www.smiledsoft.com/demos/phpzip/
January 25th, 2006 - 19:48
How can you put all the files of a directory into a zip file without knowing the files?
September 14th, 2006 - 12:09
Hello,
Is there a limit to the number of files that can be zipped up as I am using this script and I get an error “Allowed memory size of 16777216 bytes exhausted” when trying it with a large number of files.
Thanks
December 4th, 2006 - 21:41
Do somebody nkow how could we ask the script to copy the generated zipped file somewhere else on the server rather than user the headers to force the download of the file?
January 24th, 2008 - 01:14
The poster commented in his alternate solution:
.
“I did some more testing with the code I posted yesterday and found it isn’t 100% compatible with some unzip programs. Works with unix unzip, Mac OS X default tool, WinZip, WinRAR but annoyingly not with the default XP zip folder thing.”
I had this problem as well but then I discovered that I was accidentally putting superfluous data in my zip file — some php warning messages were being included as part of the zip file that the user was downloading.
I cleaned up the warnings and now my zip files are working even with the default XP zip folder thing (I don’t actually have a better name for that than the one used above
If you have a problem with the zip file you create using this script open the zip file with wordpad and take a look to see if you aren’t accidentally putting in data that you don’t need.
W
February 15th, 2008 - 20:26
Does this lib support password protection for the zip.?
May 13th, 2008 - 16:13
Problem at hand was to use PEAR Spreadsheet Excel Writer to have php generate multiple spreadsheets, put them in a zip file, and provide the zip file as a download to a user. After about 2 days I got it all to work including using IE.
Basically, first I generate the spreadsheets and save them in a tmp location on the server. Then I insert them into a zip file class one by one and delete the original spreadsheet files. After repeating the process for a few spreadsheets, I dump the zip file.
Hope it helps someone. Here’s the code:
===================================================================
export.php
require_once ‘class.zipfile.php’;
require_once ‘Spreadsheet/Excel/Writer.php’;
// Generate zip file with containg current spreadsheet file
$zipfile = new zipfile();
//$filedata = ‘testing direct input’;
// Zip file http headers
$user_browser = $root->check_user_browser();
if ($user_browser == ‘IE’) header(“Content-type: application/zip”);
else header(“Content-type: application/octet-stream”);
header(“Content-disposition: attachment; filename=\”filename.zip\”");
header(“Expires: 0″);
header(“Cache-Control: must-revalidate, post-check=0,pre-check=0″);
header(“Pragma: public”); // IE doesn’t work without this
foreach($report_names as $name) {
$filename = $name;
// Generate report for current name and save to /tmp
include ‘./export.excelfile.php’;
// Add temp spreadsheet from the include above to the zip file
$filedata = implode(“”, file(‘/tmp/’.$filename.’.xls’));
// Add the filedata to the zip file with the correct filename
$zipfile->add_file($filedata, $filename.’.xls’);
// delete the current file from temporary storage
unlink(‘/tmp/’.$filename.’.xls’);
}
// Send the zip file to browser
echo $zipfile->file();
===================================================================
export.excelfile.php
Note: “/tmp” must be world writable, ie. apache must be able to write (777)
// Save current spreadsheet to the server hard drive
unset($workbook);
$workbook = new Spreadsheet_Excel_Writer(‘/tmp/’.$filename.’.xls’);
$workbook->setVersion(8);
$workbook->setTempDir(/tmp);
// Set styles and formats
include “export.format.php”;
$worksheet =& $workbook->addWorksheet(’sheet_name’);
$worksheet->setLandscape();
// Grab actual data
$query = ‘SELECT DISTINCT * FROM `’ . $table_name . ‘`’;
$res = $db->_query($query);
// Data Header Row and similar for data
for ($i = 0; $i mysql_num_fields($res); ++$i) {
$worksheet->writeString(0, $i, $db->mysql_field_name($res, $i), $formats[DATA_HEADER]);
}
// Close the file
$workbook->close();
===================================================================
export.format.php
// defines and custom colours
define(‘DATA_HEADER’, 1);
$workbook->setCustomColor(63, 117, 151, 170); // header text color
// Global style and font
$formats = array();
$common_format = array(‘font’ => ‘Arial’,
’size’ => 8,
‘align’ => ‘center’,
‘valign’ => ‘bottom’,
‘textwrap’ => 1
);
// EXCEL DATA HEADER
$formats[DATA_HEADER] =& $workbook->addformat($common_format);
$formats[DATA_HEADER]->setColor(1); // text color
$formats[DATA_HEADER]->setBold();
$formats[DATA_HEADER]->setFgColor(63); // cell color
$formats[DATA_HEADER]->setBorderColor(60); // border styling
$formats[DATA_HEADER]->setTop(2);
$formats[DATA_HEADER]->setBottom(2);
$formats[DATA_HEADER]->setLeft(1);
$formats[DATA_HEADER]->setRight(1);
===================================================================
class.zipfile.php
by Eric Mueller
http://www.themepark.com
http://www.devco.net/archives/2005/05/24/creating_zip_files_with_php.php
http://www.devco.net/code/zipfile.inc.txt
August 27th, 2008 - 16:29
but where is the zipfile.inc.php
September 10th, 2008 - 10:15
Oh thats nice, i was in search of such a code that can generate dynamically the .zip file, i got it here, thanks for sharing
October 17th, 2008 - 01:33
Hi,
Here is code for a web page to download multiple files in a zip.
require_once(‘includes/zipfile.inc.php’);
$zipname = $_GET['setname'];
$zipfile = new zipfile();
$dir = $zipname . ‘/’;
$zipfile->add_dir( $dir );
$aFileNamesToZip = getFileArray( PATH_OUT . ‘/’ . $zipname . ‘/’, TRUE);
if ( is_array( $aFileNamesToZip ) ) {
foreach ( $aFileNamesToZip as $fileName ) {
$filePathSys = PATH_OUT . ‘/’ . $zipname . ‘/’ . $fileName;
$filePathZip = $zipname . ‘/’ . $fileName;
$filedata = implode(”, file( $filePathSys));
$zipfile->add_file($filedata, $filePathZip);
}
} else {
die(‘No files to zip.’);
}
header(‘Content-type: application/octet-stream’);
header(‘Content-disposition: attachment; filename=’ . $zipname . ‘.zip’);
echo $zipfile->file();
October 24th, 2008 - 21:46
I am trying to get this to work, I have a list of image names being queried from a table. It prompts me to save/open a zip file and the zipfile appears to have all the image *names* that I queried for but the files are all 7,136 bytes which is WRONG, it appears to *not* be pulling the actual filedata into the zip. image1 below is the name of the image file.
Here is my code:
if (tep_db_num_rows($results) > 0) {
while ($row = tep_db_fetch_array($results)) {
$zipfile->add_file($filedata, “images/”.$row['image1']);
}
}
header(“Content-type: application/octet-stream”);
header(“Content-disposition: attachment; filename=images.zip”);
echo $zipfile->file();
December 3rd, 2008 - 03:40
This post and number of comments to this site proves that sleep research/knowledge is lacking in humanity. The researchers need to focus on this subject and enlighten us all. Most of us spend one third of our lives in sleep, we should know more about it.
January 30th, 2009 - 05:08
Great code. It works perfect to send the zip to the browser, but did anyone figure out how to save this zip file to the server rather than send it to the browser?
I would like to create the zip, save it to the server, email it to an address, then delete it from the server.
February 2nd, 2009 - 14:08
Hi Gerry
Please Find the Modified script for saving to disk and not downloading:
require (“incl/zipfile.inc.php”);
$zipfile = new zipfile();
$filedata = implode(“”, file(“incl/zipfile.inc.php”));
$zipfile->add_dir(“incl/”);
$zipfile->add_file($filedata, “incl/zipfile.inc.php”);
// set file to write
$file = ‘incl/zipfile.zip’;
// open file
$fh = fopen($file, ‘w’) or die(‘Could not open file for writing!’);
// write to file
fwrite($fh, $zipfile->file()) or die(‘Could not write to file’);
// close file
fclose($fh);
February 2nd, 2009 - 15:25
Hi Gerry
Please note PHPmailer is does not come with php by deafult
Please Find the Modified script for saving to disk,mailing and deleteing:
require (“incl/zipfile.inc.php”);
require (“incl/class.phpmailer.php”);
$zipfile = new zipfile();
$filedata = implode(“”, file(“incl/zipfile.inc.php”));
$zipfile->add_dir(“incl/”);
$zipfile->add_file($filedata, “incl/zipfile.inc.php”);
// set file to write
$file = ‘incl/zipfile.zip’;
// open file
$fh = fopen($file, ‘w’) or die(‘Could not open file!’);
// write to file
fwrite($fh, $zipfile->file()) or die(‘Could not write to file’);
// close file
fclose($fh);
$senderemail = ‘You@yourdomain’;
$sendername = ‘your name’;
$receiveremail = ‘resipiant@domian’;
$receivername = ‘resipiant name’;
$attachment=($file);
$smtp_username= ”;
$smtp_password = ”;
$smtp_server = ‘192.168.2.52′;
$mail_subject = ‘My Mail Subject’;
$mail_html = ‘true’;
$mail_body = ‘My Mail Body’;
//do mailing here
$mail = new PHPMailer();
$mail->From = $senderemail;
$mail->FromName = $sendername;
$mail->AddAddress($receiveremail, $receivername);
$mail->AddAttachment($attachment);
// Fill in Username and Password for servers requiring authentication
$mail->Username = $smtp_username;
$mail->Password = $smtp_password;
// SMTP server name
$mail->Host = $smtp_server;
$mail->Mailer = “smtp”;
$mail->Subject = $mail_subject;
$mail->IsHTML = $mail_html;
$mail->Body = $mail_body;
if(!$mail->Send()) $results = ‘Error message’;
else $results = ‘Success message’;
//Delete the file
Print($results);
unlink($file);
February 4th, 2009 - 14:28
Anyone else having a problem upzipping archives created in any of these examples within OSX?
First, the download of the zip never finishes and safari complains about it not being a valid download.
Then, unzipping the created file with the default unarchive tool within OSX claims “Error 1 – Operation Not Permitted”
Lastly, if you use StuffIT Expander it unzips with no problem.
Downloads, unzips, etc without any problem on my XP box,
Any ideas?
February 15th, 2009 - 08:39
Well done. Cool job. Thanks a lot.
February 23rd, 2009 - 20:04
Hi Steven, i had the same issue (Error 1 – Operation Not Permitted), and solved it with a modified version of this library, that you can find in phpMyAdmin (i have v. 2.10.2).
I found something, similar to the file i found, here:
http://www.forosdelweb.com/f18/zip-lib-php-archivo-zip-vacio-431133/
Hope it helps
February 24th, 2009 - 19:16
Hi,
I have to archive some rather large files (80+MB already compressed)
and I was wondering if somebody had a solution where by I could simply add 2 of these files into a zip file.
The main obstacle to using most zipping scripts is that some servers have a memory usage limit. When that limit is too low (as would be the case with most shared hosting), php can’t do the job.
I was wondering if adding a file without trying to compress would help.
Any ideas?
February 28th, 2009 - 00:51
Hello,
Is there a limit to the number of files that can be zipped up as I am using this script and I get an error “Allowed memory size of 16777216 bytes exhausted” when trying it with a large number of files.
Thanks
angela
It depends on your PHP settings. Normally you can increase it with
ini_set(‘memory_limit’, ‘32M’);
as the first line of your script. At the moment you have 16M, so 32M should do.
March 4th, 2009 - 13:53
I m using this script and the archive_zip class available from pear.php.net. When i make archives of jpgs and gifs, i get the zip file but if i try with mp3s ,avis ,sis files i always get error in file creation how can i solve this problem? Should i put above script and change the class?
March 16th, 2009 - 17:17
Hi
I have a script named ‘SECURELOADS’ – it work well except when it is required to zip and download a number of large files together – zipping and downloading a few is now problem, but any attempt to go behond more than a few I get errors like:
Fatal error: Allowed memory size of 524288000 bytes exhausted (tried to allocate 129871054 bytes) in /var/www/vhosts/intentionalnoise.co.uk/httpdocs/sld2/core/hclib/compress/zipfile.php on line 120
the code in the file is below, any ideas as to why it should be doing this?
I have allocated 500mb or ram to it – why is it so hungry?
March 29th, 2009 - 07:11
@Rafael Miranda,
I have spent hours trying to figure out this problem. Your suggestion fixed it! Thanks!
-Ben
April 16th, 2009 - 23:47
I believe it would be wise to disallow files with the extension *.php if using this script in a GET method that is visible.
Using another zip file creator, I was very easily able to download raw php source in a zip package.
It’s a wonderful script, but if every used in a get method, would be like an open door to a ……possum?
August 4th, 2009 - 11:04
Hi, firts of all, great code. Very simple and very usefull. But I have a problem when I try to create more than four dirs with add_dir function, can you help me?
Thanks and again good job.
Rody
October 15th, 2009 - 16:30
Okay guys, a couple of things…
1. The add_dir limitation issue –
Steps:
– Create a catchall directory to maintain all of the user based folders/subdirectories created per user/per instance.
– Create/delete files on an indivudal site upload basis, which can easily be accomplished with mkdir( ) and rmdir( ) functions. This will allow you to set automated cycles as to how long the files stay on your system with the user having access to them during the duration.
– Use the file zip system on this posting to take care of the required zip process and output.
2. On the . php files being output in the zip drive as Ben suggested –
I have seen that issue before. Don’t put your files in the same directory that you are targeting as your holding directory. You will be able to eliminate that issue by storing your .php files in another directory and calling them to action with a simple include ( ) or require ( ) function. Note that in most cases, due to header code classes, I suggest using include_once( ) or require_once ( ) to prevent the loop causing errors.
By calling the process from another source, you protect your code from being hand delivered to the person receiving the zip.
October 15th, 2009 - 17:15
By the way, sometimes, it is an issue of server setup, php version and just outright clashing of code to have the rmdir() function remove the content but not the actual folder/directory itself.
The folder could still be there and as it is empty at that point, php will give you a warning on this. To prevent that from happening, use the ‘ unlink ( ) function, like the code that follows:
Just a little touch of overkill to save some grief later.
October 15th, 2009 - 17:20
Just so that it is clearly noted, rmdir( ) in php 5 and higher does remove the directory and directory folder completely, which is for those still using php 4 or lower.