Comments 59
А можно передать отправку файла чему-нибудь более заточенному для работы со статикой и медленными клиентами, например nginx:
header(«X-Accel-Redirect: ».$file);die;
header(«X-Accel-Redirect: ».$file);die;
Метод эквивалентный третьему, но с nginx. Удивлен, что не увидел его в статье.
Всегда использовал этот метод, т.к. веб сервер и предназначен для отдачи файлов клиенту.
Для каждой задачи должен использоваться подходящий инструмент.
А первые 2 метода я бы не публиковал. Вижу строчку, которая будет необоснованно кушать лишнюю память и не читаю код дальше.
Для каждой задачи должен использоваться подходящий инструмент.
А первые 2 метода я бы не публиковал. Вижу строчку, которая будет необоснованно кушать лишнюю память и не читаю код дальше.
Первые два метода не только плохи, но и легко кладут сервер на лопатки при средней нагрузке.
Я думаю есть проекты, где нет возможности ставить свои модули для Apache или тот же Nginx.
Не верю. Приведите примеры. Берем Apache и связываем с nginx. Апач обрабатывает динамику, nginx берет на себя статику.
Если хостинг не предоставляет необходимых инструментов, в топку такой хостинг.
Если хостинг не предоставляет необходимых инструментов, в топку такой хостинг.
Не нужна даже средняя нагрузка. Достаточно большого файла и 1-2 клиентов на медленном канале, соответственно с бешеным (иначе на плохом канале совсем не жизнь) download manager, качающим в несколько потоков.
А разве первые два метода не отдают файл постепенно? Мне кажется, несколько потоков тут никакого профита не дадут.
В том то и дело, что они отдают постепенно и пока не отдадут полностью процесс (поток) висит. А кол-во таких процессов ограничено настройками веб-сервера, т.е. если качать кол-во файлов равное кол-ву процессов, то сайт перестанет отвечать.
Подскажите, при этом методе работает докачка?
Кто-нибудь подскажет, как быть с IIS?
У меня файл лежит на диске с уникальным именем, но в момент отдачи клиенту имя файла меняется на вменяемое, поэтому приходится делать что-то, типа первого варианта. А хотелось бы обойтись без php. Это реально?
У меня файл лежит на диске с уникальным именем, но в момент отдачи клиенту имя файла меняется на вменяемое, поэтому приходится делать что-то, типа первого варианта. А хотелось бы обойтись без php. Это реально?
Вы можете указать любое имя, например:
Оно не должно совпадать с именем на сервере. Пользователю браузер должен показать именно
header('Content-Disposition: attachment; filename=FAKE_NAME.TXT');
Оно не должно совпадать с именем на сервере. Пользователю браузер должен показать именно
FAKE_NAME.TXT
Это я знаю, хотелось как раз обойтись при отдаче файла БЕЗ PHP — каким-нибудь хитрым редиректом именно в IIS,
что-нибудь типа
header(«x_redirect:real_name,fake_name), чтобы после этого сработали какие-нибудь механизмы IIS или расширения и файл бы отдался штатными средствами, но с новым именем.
что-нибудь типа
header(«x_redirect:real_name,fake_name), чтобы после этого сработали какие-нибудь механизмы IIS или расширения и файл бы отдался штатными средствами, но с новым именем.
В заголовке.
с помощью PHP
не ясно, за что минус человеку влепили. Явно вопрос был, об альтернативах XSendFile и X-Accel-Redirect под IIS.
Посмотрите здесь.
www.helicontech.com/ape/
Посмотрите здесь.
www.helicontech.com/ape/
А как быть с русскими символами вместо FAKE_NAME.TXT?
Можно попробовать отталкиваться от RFC2231 и заворачивать примерно так:
header("Content-Disposition: attachment; filename*=\"utf8'ru-ru'кириллический utf8 текст\"");
Пользователь remal рекомендует статью на stackoverflow
Последний метод хорош, но негодится, если нужно ограничить доступ к файлу (только для авторизованных пользователей). Как решение — нужно создавать симлинк на файл с произвольным именем и периодически подчищать симлинки
Последний метод у меня как раз используется в системе, где все файлы приватные. Доступ к директории закрыт в
.htaccess
. Файлы отдаются PHP скриптом, который делает все проверки (в реальности у меня Drupal).Извините, симлинки это тотальный капец, и главное зачем?
Сверху nginx, за ним любой сервер. Предположим, что все «файлы» просятся из папки /files/. Например /files/some-file.file.
В конфиге nginx'а говорим, что такие файлы нао спросить у backend'а по такому то пути. На беке, хоть средствами того же php, проверяем авторизацию пользователя, если все ок — выдаем заголовки с mime-type и т.п. и через x-accel-redirect выдаем на nginx реальное имя файла, вычисленное каким-то кастомным образом из some-file.file. Для таких файлов в ngixn делаем internal location. Если юзер не авторизован или еще что-то не так, выдаем backend'ом 403, 404 или что вам там нужно
Сверху nginx, за ним любой сервер. Предположим, что все «файлы» просятся из папки /files/. Например /files/some-file.file.
В конфиге nginx'а говорим, что такие файлы нао спросить у backend'а по такому то пути. На беке, хоть средствами того же php, проверяем авторизацию пользователя, если все ок — выдаем заголовки с mime-type и т.п. и через x-accel-redirect выдаем на nginx реальное имя файла, вычисленное каким-то кастомным образом из some-file.file. Для таких файлов в ngixn делаем internal location. Если юзер не авторизован или еще что-то не так, выдаем backend'ом 403, 404 или что вам там нужно
В случае аудио или видеофайлов первый метод не подойдет, так как браузер запрашивают файлы по частям, передавая заголовок Range. Придется обрабатывать его вручную, как описано в статье, но проще воспользоваться заголовками X-Accel-Redirect / X-SendFile.
>>> Файл читается в внутренний буфер функции readfile(), размер которого нигде не указан (может кто подскажет)
Функция readfile использует внутреннию функцию php_stream_passthru для отправки файла в output буффер. Сама по себе функция php_stream_passthru это алиас на функцию _php_stream_passthru в коде которого есть декларация буффера размером 8192 символов. Это означает что файл читаеться и отправляеться порция по 8 кБ.
Пруф линки:
github.com/php/php-src/blob/master/ext/standard/file.c#L1345
github.com/php/php-src/blob/master/main/php_streams.h#L448
github.com/php/php-src/blob/master/main/streams/streams.c#L1378
Функция readfile использует внутреннию функцию php_stream_passthru для отправки файла в output буффер. Сама по себе функция php_stream_passthru это алиас на функцию _php_stream_passthru в коде которого есть декларация буффера размером 8192 символов. Это означает что файл читаеться и отправляеться порция по 8 кБ.
Пруф линки:
github.com/php/php-src/blob/master/ext/standard/file.c#L1345
github.com/php/php-src/blob/master/main/php_streams.h#L448
github.com/php/php-src/blob/master/main/streams/streams.c#L1378
header('Content-Type: application/octet-stream');
А еще можно читать HTTP-RANGE и отдавать то, что запросил клиент, тогда получим докачку файлов.
Допустим, мы скриптом отдаем картинку. В хэдере пишем Content-Type: image/jpeg, но не указываем сontent-disposition (не attachment). Браузер ее корректно отображает. Но при ручном сохранении картинка сохраняется под именем имя_скрипта.php. Как предложить корректное имя?
Можно сделать так:
Делаем новый обработчик пути в системе, например
Работать будет только с включенным
Для браузера путь картинки будет обычным, соответственно при сохранении будет показано имя файла.
Идея нагло подсмотрена в Drupal (обработка приватных файлов).
Делаем новый обработчик пути в системе, например
/file/private
. Далее все запросы к картинкам делаем через него в формате /file/private/path/inside/your/private/directory/picture.jpg
. Обработчик пути должен выдать картинку исходя из параметров переданных скрипту.Работать будет только с включенным
mod_rewrite
. Для браузера путь картинки будет обычным, соответственно при сохранении будет показано имя файла.
Идея нагло подсмотрена в Drupal (обработка приватных файлов).
Кажется, таким поведением грешит только хром.
Я пользуюсь как раз решением, описанным в комментарии выше (расширение в урле).
От себя хочу добавить: в таком случае лучше ещё сделать и правильное имя файла, чтобы у сохраняющего не получились файлы типа picture(5).jpg
Я пользуюсь как раз решением, описанным в комментарии выше (расширение в урле).
От себя хочу добавить: в таком случае лучше ещё сделать и правильное имя файла, чтобы у сохраняющего не получились файлы типа picture(5).jpg
полные заголовки таких ответов бы привели что ли
// сбрасываем буфер вывода PHP, чтобы избежать переполнения памяти выделенной под скрипт // если этого не сделать файл будет читаться в память полностью! if (ob_get_level()) { ob_end_clean(); }
output buffering может быть вложенным, поэтому правильным вариантом будет такой:
while (ob_get_level()) {
ob_end_clean();
}
Согласно заголовку: «Отдаем файлы эффективно с помощью PHP»
стоило бы провести хоть маленькое исследование на предмет эффективности.
стоило бы провести хоть маленькое исследование на предмет эффективности.
первые два метода — это как отдавать НЕ ЭФФЕКТИВНО
так что тут проводить на эффективность нечего
так что тут проводить на эффективность нечего
мой опыт работы говорит тоже самое,
но! вдруг, автор знаетособое конфуособые настройки, чтобы эти методы нормально работали.
и поэтому тестирование хоть какой либо нагрузки, помогло бы автору, закончить статью. и написать реалии использования того или иного метода
но! вдруг, автор знает
и поэтому тестирование хоть какой либо нагрузки, помогло бы автору, закончить статью. и написать реалии использования того или иного метода
Третий метод должен был быть первым.
В целом статья хорошая, все правильно расписано про заголовки, но файлы отдавать напрямую РНР — это Зло, нет — это даже очень Большое Зло! Так что первые два пункта, можно рассматривать только как пример того, как не надо делать.
Файлы отдавать надо напрямую с WEB сервера специальными модулями, если это не аналог рапидшара, в которой куча серверов на отдачу и сделана принудительная задержка на отдачу контента для бесплатных скачиваний.
Как в принципе и закачивать надо средствами WEB сервера а не РНР.
Разработано куча специальных модулей. Я с Апачем не работаю лет уже как пять, и кроме XSendFile — я не знаю, но для nginx есть модули: X-Accel-Redirect,
Для закачки надо использовать ngx_upload_module — на эту тему статьи были на Хабре
Файлы отдавать надо напрямую с WEB сервера специальными модулями, если это не аналог рапидшара, в которой куча серверов на отдачу и сделана принудительная задержка на отдачу контента для бесплатных скачиваний.
Как в принципе и закачивать надо средствами WEB сервера а не РНР.
Разработано куча специальных модулей. Я с Апачем не работаю лет уже как пять, и кроме XSendFile — я не знаю, но для nginx есть модули: X-Accel-Redirect,
Для закачки надо использовать ngx_upload_module — на эту тему статьи были на Хабре
для создания защищенной зоны от скачивания есть модули accesskey wiki.nginx.org/HttpAccessKeyModule
отдача приватных файлов организуется с помощью этих двух модулей
отдача приватных файлов организуется с помощью этих двух модулей
а что если мне нужно отдать юзеру файл не со своего сервера а с внешнего? + учесть вариант «докачки» файла?
и, важный момент, редирект на сам файл не считается =)
и, важный момент, редирект на сам файл не считается =)
UFO just landed and posted this here
Не только с mod_proxy: redmine.lighttpd.net/projects/1/wiki/X-LIGHTTPD-send-file
Еще, полезно было бы упомянуть про ETag и набор http-функций для избежания велосипедов, в частности эти:
http_cache_etag();
http_send_file();
http_cache_etag();
http_send_file();
Первые два варианта полезны и даже очень, если нет web сервера вообще, а есть работа с клиентом на микроконтроллере, только переписать пришлось на С++.
Надо только отметить, что контент диспозишены и прочее надо отсылать не в составе страницы, а в ответе сервера.
Sign up to leave a comment.
Отдаем файлы эффективно с помощью PHP