Xoozlez
Xoozlez

Reputation: 21

Php download zip archive big filesize

Hihi,

I have a form with multiple check boxes where it allow user to select the brochures they want before they are prompted to download the zip file. The zip archive download function is working without problem. But I realized the download session will be terminated without downloading the entire file (say 8MB) and the downloaded zip file will turn out corrupted.

Referring to the codes below, it does not solve the problem. Anybody can advise what are the settings I need to look into or am I missing out any functionality?

if($send) {     
    // Make sure program execution doesn't time out
    // Set maximum script execution time in seconds (0 means no limit)
    set_time_limit(0);

    $post = $_POST;     
    $file_folder = "pdf/";  // folder to load files
    $zip = new ZipArchive();    // Load zip library 
    $zip_name = "File-".time().".zip";          // Zip name
    if($zip->open($zip_name, ZIPARCHIVE::CREATE)!==TRUE){       // Opening zip file to load files
        $error .=  "* Sorry ZIP creation failed at this time<br/>";
    }
    foreach($post['brochure'] as $file){                
        $zip->addFile($file_folder.$file);          // Adding files into zip
    }
    $zip->close();
    if(file_exists($zip_name)){
        // set headers push to download the zip
        header("Pragma: public");
        header("Expires: 0");
        header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
        header("Cache-Control: public");
        header('Content-type: application/zip');
        header("Content-Transfer-Encoding: Binary");
        header('Content-Disposition: attachment; filename="'.$zip_name.'"');
        header("Content-Length: ".filesize($zip_name)); 
        readfile($zip_name);
        // remove zip file is exists in temp path
        unlink($zip_name);

        $fp = @fopen($zip_name, "rb");
        if ($fp) {
         while(!feof($fp)) {
             print(fread($fp, 1024*8));
             flush(); // this is essential for large downloads
             if (connection_status()!=0) {
                 @fclose($file);
                 die();
             }
         }
         @fclose($file);
        }
    }

Upvotes: 1

Views: 2445

Answers (3)

Xoozlez
Xoozlez

Reputation: 21

Manage to resolve my problem with the codes below. Basically I shouldn't have unlink the file before calling for fopen.

if($send) {
    // Make sure program execution doesn't time out
    // Set maximum script execution time in seconds (0 means no limit)
    set_time_limit(0);

    $post = $_POST;     
    $file_folder = "pdf/";  // folder to load files
    $zip = new ZipArchive();    // Load zip library 
    $zip_name = "File-".time().".zip";          // Zip name
    if($zip->open($zip_name, ZIPARCHIVE::CREATE)!==TRUE){       // Opening zip file to load files
        $error .=  "* Sorry ZIP creation failed at this time<br/>";
    }
    foreach($post['brochure'] as $file){                
        $zip->addFile($file_folder.$file);          // Adding files into zip
    }
    $zip->close();
    if(file_exists($zip_name)){

        // set headers push to download the zip
        header("Pragma: public");
        header("Expires: 0");
        header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
        header("Cache-Control: public");
        header('Content-type: application/zip');
        header("Content-Transfer-Encoding: Binary");
        header('Content-Disposition: attachment; filename="'.$zip_name.'"');
        header("Content-Length: ".filesize($zip_name)); 

        //readfile($zip_name);
        // remove zip file is exists in temp path
        //unlink($zip_name);

        $fp = @fopen($zip_name, "rb");
        if ($fp) {
         while(!feof($fp)) {
             echo fread($fp, 8192);
             flush(); // this is essential for large downloads
             if (connection_status()!=0) {
                 @fclose($zip_name);
                 die();
             }
         }
         @fclose($zip_name);
        }
        unlink($zip_name);
    }
}

Upvotes: 0

Skrol29
Skrol29

Reputation: 5552

First of all, your if ($fp) {...} block should never be executed because you've done a unlink($zip_name); just before. So this block is useless.

Secondary, I suggest that you replace

readfile($zip_name);

with

ob_clean();
flush();
readfile($zip_name);

This will prevent for unexpected border effects.

And finally, if you still have the corrupted archive (I believe that), then I think it is because some PHP error has occurred before, during or after the download flushing (please note that your PHP code doesn't seem to be ending after the download, so the code may continue and some error may happen after). If such a PHP error or PHP notice has occurred, the PHP error message has been output with streaming. This is easy to check: open the corrupted archive with a text editor in order to see the binary contents, and then the PHP error message should appear clear and readable at the very start or the very bottom of the binary contents of the archive. No need to open the archive in HEXA mode.

Upvotes: 0

PoulsQ
PoulsQ

Reputation: 2016

instead of making a "readfile()" did you try this kind of solution ?

if(file_exists($zip_name)){
    header("Pragma: public");
    header("Expires: 0");
    header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
    header("Cache-Control: public");
    header('Content-type: application/zip');
    header("Content-Transfer-Encoding: Binary");
    header('Content-Disposition: attachment; filename="'.$zip_name.'"');
    header("Content-Length: ".filesize($zip_name)); 

    echo file_get_contents($file);
    exit
}

For me it's simpler and it worked fine for me at many times.

After this the problem i see is that you make :

unlink($zip_name);
$fp = @fopen($zip_name, "rb");
if ($fp) {
    ...
}

This part of the code should never go on, because you unlink the file before opening it !

Upvotes: 1

Related Questions