• Zip files using PHP without the PHP zip function: PHP Zip Class

    The search for a PHP class to zip files has taken me well over 2 years. Going from not being able to find ANY to being able to find some that worked but wouldn’t unzip on MAC, the search seemed as if it would go on forever. A lot of web hosts don’t have the PHP zip function installed yet creating zip’s is quite important and handy to a lot of project. Today my search is finally over and I have finally found an easy + working PHP class for zipping files.

    Download The Class

    Upload the unzipped class to your site (Click here to download the class)

    The Code – Download File

    The following is the base code of how the script works. All you need to do is replace the $fileonserver and $filename to the file(s) you wish to zip.

    include("zip_min.inc");
    $zipfile = new zipfile();
    $fileonserver = "path/to/file/oldfilename.txt";
    $filename = "newfilename.txt";
    $zipfile -> addFile(file_get_contents($fileonserver), $filename);
    // Force download the zip
    header("Content-type: application/octet-stream");
    header("Content-disposition: attachment; filename=test.zip");
    echo $zipfile -> file();

    You may add more files by repeating the “$zipfile -> addFile” script with more files. The above will send the file straight to the user (not create a file on your server).

    The Code – Save File To Server

    You can save the zip to the server by using the file_put_contents() function as follows:

    include("zip_min.inc");
    $zipfile = new zipfile();
    $fileonserver = "path/to/file/oldfilename.txt";
    $filename = "newfilename.txt";
    $zipfile -> addFile(file_get_contents($fileonserver), $filename);
    $contents = $zipfile -> file();
    file_put_contents("test.zip", $contents);

    As you can see it’s a short and simple process. Script seems to work quite fast too. I’ve tested the downloaded files on both Mac and Windows and it seems to work beautifully.

    Notes

    • If you get an error with memory you’ll need to allow more PHP memory by updating your php.ini file or by using a .htaccess script. (Google it)
    • Requires gzcompress PHP function (most hosts have this installed already)
    • If you change the file name of the download in the header script make sure there are no spaces in the file name.

    Tags: , ,

46 Comments


  1. Hi Kim,

    Thanks for your comment.

    This class is much the same, as it creates the file on the fly only reads out the file contents and doesn’t create a file (unless you tell it to). So it is much the same as the class you’re using now except it is Mac friendly.

    I’m very much a fan of not having to make customers do work (ie download a specific app) to access what they need. Hence why I continued the search until I found a class that works on Mac.

    Very much worth you looking into.

    Thanks Again!
    Jason S

  2. Kim Emax says:

    Well the disadvantage of this class is that it creates a file and if you can’t delete it again automatically (of safe mode is on for instance) then you’ll have to clean up manually.

    I’m using a different class that creates zip on the fly in memory. That of course have the disadvantage of using a lot of memory while creating the zip file.

    I too had the problem on mac, it’s the BomArchiver that’s the problem, on commandline unzip works just fine. My customers solution is to inform users of the flaw in BomArchiver and suggest for instance guitar or Zipeg, if unzip is not a solution.

    Kind regards.
    Kim Emax

  3. Kim Emax says:

    Well in that case I better have another look at it. From the example here it just shows that the Addfile() function takes a physic file as a param. I too am very much into not demanding specific programs has to be installed by the enduser. On the other hand I suggested it to my customer in order to prevent him from using too many development hours on a workaround for a mac issue with a specific program. Like if a customer would complain on the chaotic view if he looked at a linux maillog or apache logfile in notepad ;-)

    So if you make the zipfile on the fly, do you add a filesize header too? That troubled me a bit so it’s been skipped for now…
    /Kim

  4. I haven’t looked too much into the filesize for it because no actual file is being created, so it would be tricky getting the filesize without there being a physical file. Would be better to have it in there, if I can find a solution one day I’ll add it in.

    Cheers
    Jason S

  5. Jörn says:

    Hi,
    I try your script but the size of the files inside is always 0KB…
    have you a solution for it?

    here is my code
    [code]
    $zipfile = new zipfile();

    // get image-paths
    foreach($attached_imgs as $image) :
    $zipfile -> addFile(file_get_contents($image->path), $image->name);
    endforeach;

    // Force download the zip
    $zipname = urlencode($sitename) . '-' . urlencode($title) . '-' . date("YmdHi") . '.zip';
    header("Content-type: application/octet-stream");
    header("Content-disposition: attachment; filename=$zipname");
    echo $zipfile -> file();
    [/code]

    cheers
    Jörn

  6. Hi Jörn,

    I’d suggest you comment out the header and creation of the zip file, and echo each file name that’s been read out (perhaps add in if(file_exists($file)){ before you echo so you know the files that are being read out exist).

    The only reason I can see it reading out 0kb is if the files are empty (or don’t exist). So make sure the files are being read out correctly.

    Cheers,
    Jason S

  7. Shaz says:

    Hi,

    I tried the code given and it unzip the files very nicely on windows & MAC.

    Great work !!!

    But when I open the unzipped file it is empty and of 0KB. Please go thru the code below and provide any help.

    addFile(file_get_contents($filepath), $filename);

    // Force download the zip
    header(“Content-type: application/octet-stream”);
    header(“Content-disposition: attachment; filename=\”test.zip\”");
    echo $zipfile->file();
    ?>

  8. Hi Shaz,

    Make sure you put:
    $zipfile -> addFile(file_get_contents($filepath), $filename);

    This should fix your problem. If not, make sure the contents is being read out correctly.

    Hope this helps!
    Jason S

  9. Shaz says:

    Here is my full code. When I echo the file_get_contents it will show the file contents properly but again after unzip the file is of zero bytes.

    addFile(file_get_contents($filepath), $filename);

    // Force download the zip
    header(“Content-type: application/octet-stream”);
    header(“Content-disposition: attachment; filename=\”test.zip\”");
    echo $zipfile->file();

    ?>

  10. Shaz says:

    It seems the code is not getting posted properly. Hope the below code will come proper.

    include(“zip_min.inc”);
    $zipfile = new zipfile();
    $filepath = “”;
    $filename = “newfilename.txt”;
    //echo file_get_contents($filepath.$filename);exit; WHEN I ECHO THIS IT WILL SHOW ME THE FILE CONTENTS
    $zipfile->addFile(file_get_contents($filepath), $filename);

    // Force download the zip
    header(“Content-type: application/octet-stream”);
    header(“Content-disposition: attachment; filename=\”test.zip\”");
    echo $zipfile->file();

  11. Oh okay,

    I see where the confusion is now!

    You need to get the contents of the file to send it through, currently you’re just giving it the folder contents. It needs the file contents.

    Below is what you need.

    $zipfile->addFile(file_get_contents($filepath.$filename), $filename);

    Reading out the file path alone won’t send any content through, hence why your files are coming out at 0kb. I will update my blog to be less mis-leading.

    Jörn, this was also the issue with your script.

    Let me know if this fixes your issue.

    Cheers,
    Jason S

  12. Shaz says:

    Hey Thanks a lot Jason !!!

    It’s working now.

  13. Not a problem Shaz, happy to help.

    I’ve updated the blog so hopefully it’s clearer now. I also added an example of how to save the zip to the server.

    Enjoy,
    Jason S

  14. Jay says:

    There’s a version of this class floating around that has addDir (add directory) support, but I’ve found that it causes the built-in mac OSX unarchiver to die. Assuming it’s because of the addDir stuff, as the addFile code is identical.

    http://www.weberdev.com/get_example-4499.html

    Anyone find a zip class that allows you to do files and directories, AND opens on a Mac (without having to use stuffit)?

  15. Phil says:

    Great class. I had problems with that other class in my mac too. Then I found this one. I had it includes within 5 minutes and it was working perfectly. Thanks alot.

  16. Kim Emax says:

    Hi Jason,
    do you have your own server and could you try and step up the memory_limit to like 256-512mb and then create a file of for instance mp3 files that would be around 50-100mb? I have a strange problem now, when I activate a zip download in this size, I can’t navigate around on the site until the download has completed. If I, instead of pushing the file with a header, create a link to the file on the server, then I can easily nagivate on the site while the file is downloading, very odd!

    Regarding the content-length I haven’t found a way to get the filesize, when creating files on the fly, but save it to disk first, then use filesize() and then echo readfile(“test.zip”) instead of $zipfile -> file();, then you have the content-length and the user can see how far the download is. Also Transfer-Encoding: chunked is a useful header.

    Kind regards
    Kim

  17. Hi Kim,

    You can usually step up the memory limit to something like 128 or 256 using a .htaccess file or the phpini which would easily handle files of that size.

    The reason for your content-length issue not allowing navigation is because you’re constantly trying to get the content-length on a file that is being created on the fly. That is, the file is being created as you download, hence why you can’t navigate. If you absolutely need the content-length in there, then it’s best to create the file on the server first, then send it out to the user.

    Hope that makes sense :)

  18. Kim Emax says:

    hehe, I was asking if _you_ could change it, in order to test :-)

    I’ve also tried to write to the server first in order to get the content-length and the result is the same. Here’s my example: http://lps.netlinq.dk/test010/test_zip.class.php. The testlink servers the purpose of testing if you can navigate on the page without problems while the download occurs. It works fine as it’s suppose to, but later on… in the site, where it’s suppose to run it stops for further navigation while downloading, pretty annoying!

    I’ve been looking further into it and it seems that its a header problem, in the cache area. A download that doesn’t freeze the site looks like this:

    HTTP/1.x 200 OK
    Date: Sat, 03 Oct 2009 22:26:45 GMT
    Server: Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.6 with Suhosin-Patch mod_ruby/1.2.6 Ruby/1.8.6(2007-09-24) mod_ssl/2.2.8 OpenSSL/0.9.8g
    X-Powered-By: PHP/5.2.4-2ubuntu5.6
    Accept-Ranges: bytes
    Content-Length: 7083675
    Content-Disposition: attachment; filename=”Maxwell – Bad Habits (Remixes).zip”
    Content-Transfer-Encoding: binary
    Keep-Alive: timeout=15, max=100
    Connection: Keep-Alive
    Content-Type: application/zip

    via download.php, headers set in function.inc, doesn’t work

    HTTP/1.x 200 OK
    Date: Sat, 03 Oct 2009 21:41:49 GMT
    Server: Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.6 with Suhosin-Patch mod_ruby/1.2.6 Ruby/1.8.6(2007-09-24) mod_ssl/2.2.8 OpenSSL/0.9.8g
    X-Powered-By: PHP/5.2.4-2ubuntu5.6
    Expires: Thu, 19 Nov 1981 08:52:00 GMT
    Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
    Pragma: no-cache
    Accept-Ranges: bytes
    Content-Length: 35756585
    Content-Disposition: attachment; filename=”Maxwell – Bad Habits (Remixes).zip”
    Content-Transfer-Encoding: binary
    Keep-Alive: timeout=15, max=98
    Connection: Keep-Alive
    Content-Type: application/zip

    even after pragma, cache-control and expires headers has been removed from download.php, they’re still set:
    HTTP/1.x 200 OK
    Date: Sat, 03 Oct 2009 22:25:36 GMT
    Server: Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.6 with Suhosin-Patch mod_ruby/1.2.6 Ruby/1.8.6(2007-09-24) mod_ssl/2.2.8 OpenSSL/0.9.8g
    X-Powered-By: PHP/5.2.4-2ubuntu5.6
    Expires: Thu, 19 Nov 1981 08:52:00 GMT
    Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
    Pragma: no-cache
    Accept-Ranges: bytes
    Content-Length: 7083675
    Content-Disposition: attachment; filename=”Maxwell – Bad Habits (Remixes).zip”
    Content-Transfer-Encoding: binary
    Keep-Alive: timeout=15, max=96
    Connection: Keep-Alive
    Content-Type: application/zip

  19. Shaz says:

    Hi Jason,

    The below code works well on windows browsers as well as on MAC firefox. But on MAC Safari the zip file is not generated thru below code. It is downloading directly a text file instead of generating zip of that text file.

    Please do the needful.

    include(“zip_min.inc”);
    $zipfile = new zipfile();
    $fileonserver = “zip1.txt”;
    $filename = “newzip1.txt”;
    $zipfile -> addFile(file_get_contents($fileonserver), $filename);
    // Force download the zip
    header(“Content-type: application/octet-stream”);
    header(“Content-disposition: attachment; filename=zip1.zip”);
    echo $zipfile -> file();

  20. Kim Emax says:

    Shaz: try use “application/zip” as Content-Type instead, IE: header(“Content-Type: application/zip”);

  21. mongushu says:

    Hey Jason.

    Thanks for this lovely zip class.

    I would like to use this as part of a WordPress plugin.

    What is the licensing procedure for including this class in a freely distributed WordPress Plugin?

    Please let me know.

    Thanks again for your solid work.

  22. @Shaz
    Try the different header Kim suggested however all my testings been done in Safari mainly without any issues so I’d say there will be something else that is the issue. In the past Safari occasionally put .txt on the end of various files, but the problem didn’t persist and was purely a bug in Safari. So I’m not sure if that what you’re encountering, but I’m doubtful that it’s something specific to the class.

    @Mongushu
    You’re free to do this without any issues from my part. As long as it’s free – there’s no issues from me.

  23. Shaz says:

    Thanks Kim & Jason for the updates.

    I tried with “application/zip” but it didn’t worked. I tried to zip a .php file instead of text file thru below code. But on Safari instead of generating zip1.zip it is giving the option to download .php file.

    include(“zip_min.inc”);
    $zipfile = new zipfile();
    $fileonserver = “test.php”;
    $filename = “newtest.php”;
    $zipfile -> addFile(file_get_contents($fileonserver), $filename);
    header(“Content-type: application/zip”);
    header(“Content-disposition: attachment; filename=zip1.zip”);
    echo $zipfile -> file();

    I also tried to create a zip file using PHP’s inbuild zip fucntions (ZipArchive). The below code not worked either.

    header(“Pragma: public”);
    header(“Expires: 0″);
    header(“Cache-Control: must-revalidate, post-check=0, pre-check=0″);
    header(“Cache-Control: private”,false);
    header(“Content-type: application/zip”);
    header(“Content-Disposition: attachment; filename=”.basename($destination).”;” );
    header(“Content-Transfer-Encoding: binary”);
    header(“Content-Length: “.filesize($destination));
    readfile($destination);
    @unlink($destination);

    Here $destination is the folder name that has to be ziped.

    I am using safari version 3.2.3 (5525.28.3) and Mac version 10.5.7

  24. Definitely sounds specific to your version of Safari, try downloading Safari 4 and see if that gives you any grief. Probably change the header content type back to “application/octet-stream” as well.

    If you’re still having troubles if you could provide a link for me to see if I have the same downloading issue in safari then we’ll be able to work out if the issue is browser based or script based.

  25. Shaz says:

    I chekched on MAC Safari Version 4.0.3 (5531.9) with the content type “application/octet-stream”. But still giving the same problem.

    You can have a look thru the below link.

    http://75.127.135.77:10026/ziptest/zip1.php

  26. Kim Emax says:

    Shaz: try haveing “” around the zipfilename too, IE: header(”Content-disposition: attachment; filename=\”zip1.zip\);

    Also the abostrofs you use(”) looks different from mine(“)

    Do you get _the_ php file from mac or just some regular file?

  27. Kim Emax says:

    Shaz: BTW i’ve experienced several problems with Mac OS10.x with PHP and a zipclass like this one, the BomArchiver has a flaw, and the default zip utility for mac (think it’s called archive utility) relies on this function… Just be aware of that :-)

    /Kim

  28. @Shaz: It appears to be working correctly. I’ll explain what’s happening. Safari on Mac automatically unzips and removes a zip file after download (the zip files will be in your trash). Because your file is so small it’s doing this practically instantly thus appearing as it isn’t zipped. If you did some larger files I think you’ll find that it is actually getting the zip file and not just the file itself.

  29. Shaz says:

    Hi Kim,

    I tried with “” around the zipfilename. But it’s downloading the .php file instead of zip.

  30. Shaz says:

    Hi Jason,

    Thanks a lot for such a valuable explanation. I checked the Trash and it shows the zip file in trash.

  31. Kim Emax says:

    Hi Jason.

    I’ve solved my problem, it turned out to be the sessions that made the download hang, so a session_write_close() call made the trick.

    Regarding filesize, do you have any idea how to calculate it on the fly or is only solution to save the file to disk and readfile() it from there…? (that’s what I’ve come up with)

    Kind regards
    Kim

  32. Saving the file is the best and only accurate way of doing it. So I’d suggest you do that if you want to send through the file size. I’d probably suggest avoiding readfile() and just do a straight link to the file on the server to save some server resources, they’ll effectively have the same outcome.

    The PHP to do so would be reading out the following:

    Header(“HTTP/1.1 301 Moved Permanently”);
    Header(“Location: http://www.fullurltofile.com/this/file.zip“);

  33. Kim Emax says:

    …and would open the potentiel risk of distributing the link (unless the location could be outta webscope, but that wouldn’t be possible :-)

    Moved permanently is also not the way around this, it’s a one time download file based on user selected and with watermarking of each file, but that you didn’t know.

    I think the best solution is to build the file outside webscope, and readfile it from there… then delete it or run a clean up script once an hour, if safe mode is on (as it is now) if the customer want the users to see how far their downloads have progressed.

    Kind regards
    Kim

  34. I’m pretty sure you can just take out the 301 header, but if you’re removing the file after it doesn’t matter because the file won’t exist anymore – it all functions the same as if you were to read the file out. Because it’s a server side redirect, they’re not actually presented with the URL, it will just download the file.

    I’d just suggest giving it a go to save some server resources. But it’s totally up to you how you want to do it.

    Cheers
    Jason S

  35. Kim Emax says:

    I’ll give it a try, thanks Jason

  36. Henk says:

    Works great, but you can’t add directories :(

  37. Andrew Wood says:

    I very rarely put comments on people sites but today it is worth it.

    Your code works a treat on mac and pc.

    Thanks

    Andrew

  38. Frank says:

    This script just saved me!! I was using the one from Rochak Chauhan found at PHP Classes and couldn’t figure out how to implement the MAC end of it. Your script rocks. It was a piece of cake to work it into my code.

    Thanks

  39. Debopriyo says:

    Excelent code!! It saves my time :) …I can use it to add multiple files in a zip. The final zip file opens properly even in PHP+window+localserver.

  40. Daniel Demole says:

    I’m using your zip method, but still getting:

    Unable to unarchive “test.zip” into “test”.

    (Error 1 – Operation not permitted.)

    The code to zip I’m using is:

    http://gist.github.com/353351

    The archive ends up the proper size, but gives me the error. Am I missing a step?

  41. Daniel Demole says:

    Helps to get your || and && right doesn’t it :)

    Edited the code to skip both “.” or “..”. All good.

  42. Visva says:

    Hi,

    I have searched more than one days for mac os download zip issues.

    Finally, i go the your script, it’s working prefectly.

    Thanks for your support

  43. Herman says:

    Nice.

    Got something weird though. If I run it on my WAMP installation on my desktop it works fine. However if I upload it to my hosting providers Linux based server the zipfile is a bit weird.

    If I open the zipfile created from my hosting provider it seems to contain only a single file with the filename exactly the same as the zipfile itself but without an extension. If I open this with zip the actual zip comes out. It appears as if online it is zipped twice, or more likely the zipfile confuses my zip programs. I tried with WinRar and 7-zip. Both do exactly the same thing.

  44. Hi Herman,

    Make sure you have the files you’re zipping on your hosting providers site as this is likely the cause of the issue.

  45. Mike says:

    Any chance of getting the source? I really need to be able to add folders in my zip. Or, maybe, you could add the feature?

  46. Mike says:

    Nevermind, thought it was compressed.

Leave a comment