tags:

views:

720

answers:

4

Hi everyone. I'm trying to use the following code to create a zip file from a directory and serve it to the user via an http download:

 // write the file
file_put_contents($path . "/index.html", $output);

// zip up the contents
chdir($path);
exec("zip -r {$course->name} ./");

$filename = "{$course->name}.zip";

header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=' .urlencode($filename));
header('Content-Transfer-Encoding: binary');

readfile($filename);

I am able to create the zip file, but downloading it over http is not working. If I download the zip file that's created using an ftp client then Mac's Stuffit Expander unzips the files just fine, but if I download it over http, the mac unzipper creates an endless loop. What I mean by this is say the file I download is called course.zip, then unzipping the file gives course.zip.cpgz and unzipping that file gives course.zip again..and on and on.

Anyone have any ideas?

Thanks!

+1  A: 

As suggested by karim79, I'll put my comment as an answer: what happens if you change the MIME type from application/octet-stream to application/zip?

Also, I see you're using a command line zip program, but you don't check for success of the zip, and also don't check if the file exists before attempting to send it out to the end users browser. Try hard coding a file name, manually using zip to guarantee a properly formed zip file, and then see if your code will spit it to your browser properly.

Tony Miller
Ok I tried changing the MIME type to application/zip as well as hard coding in a zip file that I knew to be well-formed (a file that unzipped successfully both on my computer and on the server after uploading) and got the same result.
Eric
A: 

What you're seeing is that the archive utility is not recognizing the zip file as a zip file, and tried to zip up the zip archive itself. The second operation simply unzips the first file created, so never actually opening the file at all. This is due to the zip being corrupted.

It is possible that the browser somehow mangled the zip file (newline conversions anyone?) during the download process. As mentioned, check the mime type and use the php header() to set the correct MIME type (application/zip).

futureelite7
Ok I tried using application/zip with no luck. The browser download dialogue indicates to me that I am attempting to download a "PC Zip archive" and when it is downloaded to my machine OS X shows "Kind: ZIP Archive," but it does the same thing when I try to unzip it. (This is also with a hard-coded file that I did not zip on the fly that worked on my machine before upload)
Eric
A: 
  1. Re-zipping it every time it is requested is not a good idea. Try doing that only if the ZIP file does not exist already.

  2. If is a volatile file or just a single small file you want to transfer compressed, try using ob_start('ob_gzhandler') instead, simplier, smaller, cleaner. The file is transfered compressed, but it is saved in its original format by the client-side.

  3. Specifying the Content-Length header is needed to allow the downloader to know the end of the file, allowing progress control, detection of corruption of the file and avoiding the hang of the HTTP session (if Connection is in Keep-Alive mode), maybe the lack of this header is the root of the problem.

Havenard
I'm specifying the Content-Length header now as well and it still does not work. I'm essentially just trying to serve about 15 small files to the user at once and figured zipping was my only option. Is there another way to go about doing it?
Eric
+2  A: 

I had this problem and it turned out the downloaded zip file had a new line inserted at the very beginning.

Solved by using ob_clean and flush functions

    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/octet-stream");
    header("Content-Disposition: attachment; filename=".basename($archive_file_name));
    header("Content-Transfer-Encoding: binary");
    header("Content-Length: ".filesize($archive_file_name));
    ob_clean();
    flush();
    echo readfile("$archive_file_name");
adam