Как создать ZIP-файл, используя PHP и ZipArchive, не отправляя его в браузер?

В моем веб-приложении на PHP/MySQL есть функция, которая объединяет набор изображений для каждого пользователя в один PDF-файл с помощью ImageMagick. Каждый PDF-файл затем помещается в ZIP-файл. Каждое изображение может иметь размер до 5 МБ. Моя проблема заключается в том, что время ожидания браузера истекло до того, как пользователь сможет загрузить документ.

Есть идеи, как лучше всего это сделать?

Я думал, что смогу создать ZIP-файл, не отправляя его в браузер (т.е. удалить "заголовки" в конце моего кода), а затем отправить ссылку на файл по электронной почте; тем не менее, он все еще требует, чтобы пользователь долго ждал создания ZIP-файла (кажется, что браузер просто зависает). Может ли это быть сделано с AJAX за кулисами или непосредственно на сервере?

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

1 ответ

IMO, вы должны запустить некоторую фоновую задачу, которая сделает эту работу. Фоновый работник может, например, сохранить URL-адрес в файле результатов в БД после его завершения (может также сохранить некоторую дополнительную информацию, такую ​​как текущее состояние и ход выполнения задания), тем временем веб-страница может периодически запрашивать сервер, используя AJAX, если задание уже выполнено или нет, и, наконец, отображать ссылка, когда она будет доступна.

Самый простой способ добиться этого - запустить скрипт как фоновый процесс:

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

archiver.php должен начинаться со следующих строк:

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

это предотвратит остановку скрипта, когда родительский скрипт завершит работу.

Вторая идея, которая у меня есть, более сложна - вы можете написать демон, который будет работать в фоновом режиме в ожидании заданий. Для связи с ним вы можете использовать очереди (например, AMQP) или просто базу данных. С помощью демона вы будете иметь больше контроля над тем, что происходит и когда - при первом подходе может случиться так, что ваше приложение будет запускать слишком много процессов.

Другие вопросы по тегам