1

For reference, I have already read and tried the answers in these and several other threads:

Creating and serving zipped files with php

Opening downloaded zip file creates cpgz file?

I have a zip file on my server.

  1. When I use Filezilla to move that Zip file from my server to my Mac, I can open it normally.

  2. When I use this PHP code to download the Zip file to my Linux machine, it opens normally.

  3. When I use this PHP code to download the Zip file to my Mac, using Safari or Firefox, I get an error saying "Decompression Failed" or "The structure of the archive is damaged" or I get a .cpgz file - which I believe means that the computer is zipping the file, not unzipping it.

Here is the PHP code I am using to deliver the zip file.

$zipname = "myfile.zip";
$zippath = "/path/to/" . $zipname;

      if ($downloadzip = fopen ($zippath, "r")) {
            $fsize = filesize($zippath);

            header("Content-type: application/zip");
            header("Content-Disposition: attachment; filename=\"".$zipname."\"");
            header("Content-length: $fsize");
            header('Content-Transfer-Encoding: binary');
            #header("Cache-control: private"); //use this to open files directly

            echo fpassthru($downloadzip); // deliver the zip file

        }
        fclose ($downloadzip);

I found some headers that work. I don't really know or care why it work, I am just happy it works... I tried a ton of different things, .htaccess files, php.ini / zlib settings.

Here's the answer http://perishablepress.com/http-headers-file-downloads/

$zipName = 'myfile.zip';
$zipPath = 'mydirectory/' . $zipName;


    if (file_exists($zipPath)) {

        header("Pragma: public");
        header("Expires: 0");
        header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
        header("Cache-Control: public");
        header("Content-Description: File Transfer");
        header("Content-type: application/octet-stream");
        header("Content-Disposition: attachment; filename=\"".$zipName."\"");
        header("Content-Transfer-Encoding: binary");
        header("Content-Length: ".filesize($zipPath));
        ob_end_flush();
        @readfile($zipPath);
}
Community
  • 1
  • 1
Chris
  • 5,199
  • 15
  • 61
  • 124
  • what happens when you replace `echo fpassthru($downloadzip);` by `readfile( $zippath );` ? And have you tried removing: `header('Content-Transfer-Encoding: binary');` ? – Green Black Sep 13 '12 at 17:24
  • When I use `readfile()` it comes in at 0.2 MB which is way undersized. I have removed `header('Content-Transfer-Encoding: binary');` but it makes no difference. – Chris Sep 13 '12 at 19:07

3 Answers3

6

Often the issue is caused by extra characters that have been printed or echo'd to the page before you read out the file. Even a space will cause the failure. To fix that issue, call ob_end_clean(); before you read the file which will clear the output buffer and turn off buffering.

But keep in mind you can have nested output buffers, and this will corrupt your download as well (cheers to Vladamir for figuring this out). So to clear the output buffer completely run this before you read your file:

while (ob_get_level()) {
    ob_end_clean();
}    

This will clear out your entire buffer and you won't have any extra characters to mess up your download.

For those interested i've pasted my download script below. My zip files now download perfectly, and so far this works great.

if (file_exists($zip_file_path)) {

        header("Pragma: public");
        header("Expires: 0");
        header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
        header("Cache-Control: public");
        header("Content-Description: File Transfer");
        //We can likely use the 'application/zip' type, but the octet-stream 'catch all' works just fine.  
        header("Content-type: application/octet-stream");
        header("Content-Disposition: attachment; filename='$zip_file_name'");
        header("Content-Transfer-Encoding: binary");
        header("Content-Length: ".filesize($zip_file_path));

        while (ob_get_level()) {
             ob_end_clean();
        }

        @readfile($zip_file_path);

        exit;
}
Maximus
  • 1,237
  • 14
  • 16
5

Here is what works

$zipName = 'myfile.zip';
$zipPath = 'mydirectory/' . $zipName;

    if (file_exists($zipPath)) {

        header("Pragma: public");
        header("Expires: 0");
        header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
        header("Cache-Control: public");
        header("Content-Description: File Transfer");
        header("Content-type: application/octet-stream");
        header("Content-Disposition: attachment; filename=\"".$zipName."\"");
        header("Content-Transfer-Encoding: binary");
        header("Content-Length: ".filesize($zipPath));
        ob_end_flush();
        @readfile($zipPath);
}
Chris
  • 5,199
  • 15
  • 61
  • 124
  • nice .. my issue resolved in safari by adding `ob_end_flush();` in the end before read file. Wants to highlight, I was facing issue to download zip file along with .html extension in Safari – Shadman Mar 15 '19 at 03:59
0

Well, I presume you know that your $fsize variable is not being written to that header because it's enclosed by quotes. You could try something like this:

header('Cache-Control: public');
header('Content-Description: File Transfer');
header('Content-Disposition: attachment; filename=\"".$zipname."\"');
header('Content-Type: application/zip');
chriscct7
  • 517
  • 5
  • 11
  • $fsize should work fine, it is surrounded by double quotes, not single quotes. – Green Black Sep 13 '12 at 17:28
  • I do think the $fsize is working, when I comment-out that header line, the download does not know how big the file is. When that header line is in use, the download does know how big the file is. – Chris Sep 13 '12 at 17:30
  • @Chris my mistake. Try adding the size to the above headers I gave, and see if that works. – chriscct7 Sep 13 '12 at 17:36
  • @chriscct7 your headers give me the same end result – Chris Sep 13 '12 at 17:44