Я работаю над приложением электронной коммерции в PHP. Чтобы сохранить безопасные URL-адреса, ссылки на скачивание продукта хранятся за PHP. Существует файл, скажем download.php, который принимает несколько параметров через GET и проверяет их на базе данных. Если все идет хорошо, оно служит файлу с использованием функции readfile () в PHP.
Теперь проблема возникает, когда файл, который должен быть передан readfile (), больше, чем ограничение памяти, установленное в php.ini. Поскольку это приложение будет использоваться многими пользователями на общедоступном хостинге, мы не можем ретранслировать изменения параметров php.ini.
В наших попытках найти обходные пути я сначала подумал, что мы можем пойти на вызовы fread () в цикле while, но, похоже, это наложит проблемы, а также подчеркнет здесь. Загрузка больших файлов надежно в PHP
Поэтому мой лучший вариант – обнаружить / проверить, поддерживает ли сервер X-Accel-Redirect (в случае Nginx) / X-Sendfile (в случае Apache)
Если сервер поддерживает X-Accel-Redirect / X-Sendfile, я могу использовать их и в блоке else, я могу сообщить администратору системы об ограничении памяти, установленном php.ini
В идеале я хочу использовать серверную поддержку, такую как X-Accel-Redirect / X-Sendfile, где это возможно, и если это не сработает – я бы хотел иметь резервный код для чтения файлов без readfile ().
Я еще не уверен, как readfile () и fread () в цикле отличаются друг от друга, но кажется, что цикл будет создавать проблему, опять же, как это предлагается в разделе Загрузка больших файлов в PHP
Надеюсь получить помощь, предложения, коды, рекомендации.
Спасибо за прочтение.
Чтобы определить, установлен ли модуль apache mod_xsendfile, вы можете попробовать этот код:
if function_exists('apache_get_modules') && in_array('mod_xsendfile', apache_get_modules()) { header("X-Sendfile"); }
Но этот код просто проверяет, установлен ли только модуль, который может вызвать ошибки, если он установлен, но настроен неправильно
еще один возможный способ сделать это, чтобы настроить всю серверную переменную через Apache .htaccess:
<IfModule mod_xsendfile.c> <Files *.php> XSendFile On XSendFileAllowAbove On SetEnv MOD_X_SENDFILE_ENABLED 1 </Files> </IfModule>
и проверить его форму php-кода:
if ($_SERVER['MOD_X_SENDFILE_ENABLED']) { Header(...) }
Общая идея для nginx одинакова – просто передайте значение переменной состояния в backend через HTTP-заголовок или переменную CGI / FastCGI.
readfile не занимает большого объема памяти. Он открывает файл, читает небольшую часть, записывает эту часть в браузер и затем повторно использует память для следующего чтения. Это то же самое, что использовать fread + echo в цикле while. Вы не будете ограничены пределами памяти, но вы будете ограничены max_execution_time и т. Д.
Если вы хотите использовать поддержку X-Accel-Redirect (или аналогичную), предоставленную вашим веб-сервером, отправьте такой заголовок (для Nginx):
header('X-Accel-Redirect: /path/to/file');
Ваше приложение не может знать, поддерживает ли сервер. Вам нужно будет указать параметр конфигурации, чтобы администратор / установщик вашего программного обеспечения мог предоставлять такую информацию вручную.
Вы можете установить на сервере переменную окружения, которая контролирует правильное имя заголовка сервера (возможно, настраивается командой ops или тем, кто отвечает за создание сценариев / определение серверной среды). Это поддается любому стеку, поддерживающему файл на основе заголовка, потому что люди, контролирующие этот стек, затем могут принимать решения, пока вы просто читаете из среды заголовок.
<?php /** * Method to delegate sending file to webserver * * Uses environment variable `SERVER_ACCEL_HEADER` to define the specific * server header to use to send files from server layer rather than * application layer * * @param string $path path to file that should exist * @param bool $die if script should terminate after setting header * @return void returns nothing */ function sendPathAccel($path, $die=true) { $accelHeader = getenv('SERVER_ACCEL_HEADER'); header("{$accelHeader}: {$path}"); if($die) { die(); } }
Предупреждение: Следует отметить, что это не новичок, поэтому будьте осторожны. Многие могут ошибаться, используя это, если люди, отвечающие за серверную среду, не настраивают его, вероятно, не сработают или могут вызвать ошибки, но это просто, быстро, и я не могу придумать причину его изменения.