Как создать 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) или просто базу данных. С помощью демона вы будете иметь больше контроля над тем, что происходит и когда - при первом подходе может случиться так, что ваше приложение будет запускать слишком много процессов.