简体   繁体   中英

PHP Download Script Creates Unreadable ZIP File on Mac

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);
}

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);
}

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;
}

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');

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM