#php #nginx #export #attachment
#php #nginx #экспорт #вложение
Вопрос:
У меня есть скрипт, который берет данные из базы данных и собирает их в кэш вывода PHP (ob_start), распечатывая его. После его очистки и я получаю запрос на сохранение файла. Но файл не всегда нужной длины. Почти всегда он имеет длину 160 Кб, но иногда имеет полную длину 15 Мб. Я только что запустил этот скрипт 20 раз, и 3 раза из 20 я получил нормальный размер файла, в других случаях он имеет тот же размер (160 538 байт).
Почему это может произойти?
Короткий вариант моего кода:
ob_start();
$offset = 0;
$count = 20000;
while (true) {
$dataFromMysql = GetMySqlData($count, $offset);
foreach($dataFromMysql as $data) {
print implode(';', $data);
}
if (!$dataFromMysql || count($dataFromMysql) < $count) {
break;
}
$offset = $count;
}
header('Cache-Control: no-cache, no-store, must-revalidate');
header('Pragma: no-cache');
header('Expires: 0');
header('Content-Type: application/csv');
header('Content-Length: ' . ob_get_length());
header('Content-Disposition: attachment; filename="export.csv"');
ob_end_flush();
exit;
Сайт стоит на двух физических серверах. Но я попросил администратора направить поток на главный сервер для этого скрипта.
Комментарии:
1. сначала переместите заголовки в начало
2. Проверьте максимальное время выполнения попробуйте отправить фрагментированный файл (используя заголовок HTTP / 1.1)
3. Теперь заголовки в верхней части скрипта и добавлен заголовок (‘Content-Transfer-Encoding: chunked’); но все та же проблема…
Ответ №1:
Вместо использования функций ob_ * вы могли бы просто записать в строковую переменную, затем вы можете использовать strlen() для получения длины содержимого, а затем распечатать полную строку в конце.
Редактировать: что может быть более полезным, так это fputcsv() , вы можете использовать его для создания файла csv на стороне сервера и отправки его в конце с помощью readfile() .
Комментарии:
1. Я попытался сохранить данные в строку, php:// output, реальный файл, ob_ *, но ничего из этого не работает должным образом… вот почему я здесь)
Ответ №2:
Я победил!
Вот краткий вариант рабочего кода:
header('Content-Description: File Transfer');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Content-Transfer-Encoding: chunked');
header('Pragma: public');
header('Expires: 0');
header('Content-type: application/csv');
header('Content-Disposition: attachment; filename="export.csv"');
header('Cache-Control: max-age=0');
$outputString = '';
$offset = 0;
$count = 20000;
while (true) {
$dataFromMysql = GetMySqlData($count, $offset);
foreach($dataFromMysql as $data) {
$outputString .= implode(';', $data);
}
if (!$dataFromMysql || count($dataFromMysql) < $count) {
break;
}
$offset = $count;
}
$contentLength = strlen($outputString);
header('Content-Length: ' . $contentLength);
$hFile = fopen('php://output', 'wb ');
if ($hFile) {
for ($i=0; $i<$contentLength; $i ) {
fwrite($hFile, $this->outputString[$i]);
}
fclose($hFile);
}
exit;
Я не знаю почему, но теперь это работает! Просто потому, что я вывожу полную собранную строку на один символ (1 байт). Я попытался вывести его на 1 КБ (1024 байта), но это не сработало : (
$hFile = fopen('php://output', 'wb ');
if ($hFile) {
for ($i=0; $i<$contentLength; $i ) {
fwrite($hFile, $this->outputString[$i]);
}
fclose($hFile);
}
Я надеюсь, что это кому-то поможет 😉