www.devco.net by r.i.pienaar

24May/0533

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();
?>

About R.I. Pienaar

Systems Administrator, Consultant, Linux Guy, Automator, Ruby Coder
Tagged as: , Leave a comment
Comments (33) Trackbacks (0)
  1. What’s wrong with using gzcompress()/gzdeflate()/gzencode() in the zlib extension?

  2. 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.

  3. 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();
    #———————————————
    ?>

  4. Hi, can we use this class in commercial products ?
    I don’t see any licence info.

  5. 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.

  6. 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/

  7. How can you put all the files of a directory into a zip file without knowing the files?

  8. 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

  9. 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?

  10. 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

  11. Does this lib support password protection for the zip.?

  12. 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

  13. but where is the zipfile.inc.php

  14. 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

  15. 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();

  16. 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();

  17. 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.

  18. 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.

  19. 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);

  20. 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);

  21. 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?

  22. Well done. Cool job. Thanks a lot.

  23. 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 :)

  24. 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?

  25. 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.

  26. 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?

  27. 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?

  28. @Rafael Miranda,
    I have spent hours trying to figure out this problem. Your suggestion fixed it! Thanks!
    -Ben

  29. 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?

  30. 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

  31. 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.

  32. 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.

  33. 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.


Leave a comment


Trackbacks are disabled.