简体   繁体   中英

How to create a ZIP file using PHP and ZipArchive without sending it to the browser?

My PHP/MySQL web application has a function that combines a set of images for each user into a single PDF using ImageMagick. Each PDF is then placed into a ZIP file. Each image can be up to 5 MB in size. My problem is that the browser times out before the user can download the document.

Any ideas on how to best do this?

I was thinking I could create the ZIP file without sending it to the browser (ie remove the "headers" at the end of my code) and then email a link to the file; however, it still requires that the user wait a long time for the ZIP file to be created (it appears as if the browser is just hanging). Could this be done with AJAX behind the scenes or directly on the server somehow?

$tmp_path = sys_get_temp_dir().'/';
$archive_name = "myarchive.zip";

$zip = new ZipArchive();

if($zip->open($archive_name,1 ? ZIPARCHIVE::OVERWRITE : ZIPARCHIVE::CREATE) !== true) {
    return false;
}

foreach ($rows AS $row) {

    $irows = get_images($row['user_id']);

    $images = array();

    foreach($irows AS $irow){
        $doc = fetch_document_path($irow['id']);
        $output_file_name = $tmp_path.$irow['id'].'.jpg';  
        exec ('convert '.$doc.' '.$output_file_name);
        $images[] = $irow['id'].'.jpg'; 
    }    

    $images = implode(' ', $images);   
    $output_file_name = $tmp_path.$row['name'].'.pdf';  
    exec ('convert '.$images.' "'.$output_file_name.'"');       
    $zip->addFile($output_file_name,basename($output_file_name));

}

$zip->close();

header('Content-type: application/zip');
header('Content-Disposition: attachment; filename="output.zip"');
readfile($archive_name);

IMO you should run some background task that will do the job. Background worker can for example save URL to result file in DB after it'll finished (can also save some more information like current status and job progress), meantime webpage can periodically ask server using AJAX if job is already done or not and finally display link when it'll be available.

Simplest way to achieve that is to run your script as background process:

  $arg1 = escapeshellarg($somearg1);
  $arg2 = escapeshellarg($somearg1);
  exec(sprintf('/usr/bin/php archiver.php %s %s > /dev/null 2>&1 & echo $!', $arg1, $arg2));

archiver.php should begin with following lines:

 <?php
 ignore_user_abort(true);
 set_time_limit(0);

it'll prevent script from being stopped when parent script will finish working.

Second idea I have is more complex - you can write a daemon that will run in background waiting for jobs. To communicate with it you can use queues (like AMQP) or just database. With daemon you'll have more control on what is happening and when - in first approach it can happen that your application will fire too many processes.

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