Я пытаюсь создать функцию, которая получает путь к файлу, определяет, что это такое, устанавливает соответствующие заголовки и обслуживает его так же, как Apache.
Причина, по которой я делаю это, заключается в том, что мне нужно использовать PHP для обработки некоторой информации о запросе перед обслуживанием файла.
Скорость имеет решающее значение
virtual () не вариант
Должен работать в среде общего хостинга, где пользователь не может контролировать веб-сервер (Apache / nginx и т. Д.)
Вот что у меня есть на данный момент:
File::output($path);
<?php
class File {
static function output($path) {
// Check if the file exists
if(!File::exists($path)) {
header('HTTP/1.0 404 Not Found');
exit();
}
// Set the content-type header
header('Content-Type: '.File::mimeType($path));
// Handle caching
$fileModificationTime = gmdate('D, d M Y H:i:s', File::modificationTime($path)).' GMT';
$headers = getallheaders();
if(isset($headers['If-Modified-Since']) && $headers['If-Modified-Since'] == $fileModificationTime) {
header('HTTP/1.1 304 Not Modified');
exit();
}
header('Last-Modified: '.$fileModificationTime);
// Read the file
readfile($path);
exit();
}
static function mimeType($path) {
preg_match("|\.([a-z0-9]{2,4})$|i", $path, $fileSuffix);
switch(strtolower($fileSuffix[1])) {
case 'js' :
return 'application/x-javascript';
case 'json' :
return 'application/json';
case 'jpg' :
case 'jpeg' :
case 'jpe' :
return 'image/jpg';
case 'png' :
case 'gif' :
case 'bmp' :
case 'tiff' :
return 'image/'.strtolower($fileSuffix[1]);
case 'css' :
return 'text/css';
case 'xml' :
return 'application/xml';
case 'doc' :
case 'docx' :
return 'application/msword';
case 'xls' :
case 'xlt' :
case 'xlm' :
case 'xld' :
case 'xla' :
case 'xlc' :
case 'xlw' :
case 'xll' :
return 'application/vnd.ms-excel';
case 'ppt' :
case 'pps' :
return 'application/vnd.ms-powerpoint';
case 'rtf' :
return 'application/rtf';
case 'pdf' :
return 'application/pdf';
case 'html' :
case 'htm' :
case 'php' :
return 'text/html';
case 'txt' :
return 'text/plain';
case 'mpeg' :
case 'mpg' :
case 'mpe' :
return 'video/mpeg';
case 'mp3' :
return 'audio/mpeg3';
case 'wav' :
return 'audio/wav';
case 'aiff' :
case 'aif' :
return 'audio/aiff';
case 'avi' :
return 'video/msvideo';
case 'wmv' :
return 'video/x-ms-wmv';
case 'mov' :
return 'video/quicktime';
case 'zip' :
return 'application/zip';
case 'tar' :
return 'application/x-tar';
case 'swf' :
return 'application/x-shockwave-flash';
default :
if(function_exists('mime_content_type')) {
$fileSuffix = mime_content_type($path);
}
return 'unknown/' . trim($fileSuffix[0], '.');
}
}
}
?>
php
performance
file-io
x-sendfile
Кирк Уимет
источник
источник
$extension = end(explode(".", $pathToFile))
, или вы можете сделать это с подстрокой и strrpos:$extension = substr($pathToFile, strrpos($pathToFile, '.'))
. Кроме того, в качестве альтернативыmime_content_type()
вы можете попробовать системный вызов:$mimetype = exec("file -bi '$pathToFile'", $output);
Ответы:
Мой предыдущий ответ был частичным и плохо документированным, вот обновление с кратким изложением решений, полученных из него и других участников обсуждения.
Решения отсортированы от лучшего решения к худшему, а также от решения, требующего наибольшего контроля над веб-сервером, до решения, требующего меньшего. Кажется, нет простого способа получить одно решение, которое было бы быстрым и работало бы везде.
Использование заголовка X-SendFile
Как утверждают другие, это действительно лучший способ. Основа заключается в том, что вы осуществляете контроль доступа на php, а затем вместо того, чтобы отправлять файл самостоятельно, вы указываете веб-серверу сделать это.
Базовый код php:
Где
$file_name
полный путь к файловой системе.Основная проблема с этим решением заключается в том, что оно должно быть разрешено веб-сервером и либо не установлено по умолчанию (apache), либо неактивно по умолчанию (lighttpd), либо требует определенной конфигурации (nginx).
Apache
В apache, если вы используете mod_php, вам нужно установить модуль с именем mod_xsendfile, а затем настроить его (либо в конфигурации apache, либо в .htaccess, если вы разрешите)
В этом модуле путь к файлу может быть абсолютным или относительным по отношению к указанному
XSendFilePath
.Lighttpd
Mod_fastcgi поддерживает это при настройке с
Документация по этой функции находится на вики lighttpd, они документируют
X-LIGHTTPD-send-file
заголовок, ноX-Sendfile
имя также работаетNginx
В Nginx вы не можете использовать
X-Sendfile
заголовок, вы должны использовать свой собственный заголовок с именемX-Accel-Redirect
. Он включен по умолчанию, и единственное реальное отличие состоит в том, что его аргумент должен быть URI, а не файловой системой. Следствием этого является то, что вы должны определить местоположение, помеченное как внутреннее в вашей конфигурации, чтобы клиенты не находили реальный URL-адрес файла и не переходили непосредственно к нему, их вики содержит хорошее объяснение этого.Символические ссылки и заголовок Location
Вы можете использовать символические ссылки и перенаправлять на них, просто создавайте символические ссылки на свой файл со случайными именами, когда пользователь авторизован для доступа к файлу, и перенаправляйте пользователя к нему, используя:
Очевидно, вам понадобится способ обрезать их либо при вызове скрипта для их создания, либо через cron (на машине, если у вас есть доступ, или через какую-либо службу webcron в противном случае)
Под apache вы должны иметь возможность включить
FollowSymLinks
в.htaccess
или в конфигурации apache.Контроль доступа по IP и заголовку Location
Другой способ - сгенерировать файлы доступа к apache из php, разрешив явный IP-адрес пользователя. Под apache это означает использование команд
mod_authz_host
(mod_access
)Allow from
.Проблема в том, что блокировка доступа к файлу (поскольку несколько пользователей могут захотеть сделать это одновременно) нетривиальна и может привести к тому, что некоторые пользователи будут ждать долгое время. И вам все равно нужно обрезать файл.
Очевидно, другая проблема заключается в том, что несколько человек с одним и тем же IP-адресом потенциально могут получить доступ к файлу.
Когда все остальное терпит неудачу
Если у вас действительно нет никакого способа заставить ваш веб-сервер помочь вам, единственное оставшееся решение - это файл для чтения , который доступен во всех используемых в настоящее время версиях php и работает довольно хорошо (но не очень эффективен).
Комбинируя решения
В общем, лучший способ отправить файл очень быстро, если вы хотите, чтобы ваш php-код можно было использовать повсюду, - это иметь где-нибудь настраиваемую опцию с инструкциями о том, как активировать ее в зависимости от веб-сервера и, возможно, автоматическое обнаружение в вашей установке сценарий.
Это очень похоже на то, что делается во многих программах для
mod_rewrite
на apache)mcrypt
модуль php)mbstring
модуль php)источник
header("Location: " . $path);
?Самый быстрый способ: не надо. Загляните в заголовок x-sendfile для nginx , есть аналогичные вещи и для других веб-серверов. Это означает, что вы все еще можете управлять доступом и т. Д. В php, но делегировать фактическую отправку файла на веб-сервер, предназначенный для этого.
PS: Меня охватывает мурашки, когда я думаю о том, насколько эффективнее использовать это с nginx по сравнению с чтением и отправкой файла на php. Подумайте только, если 100 человек загружают файл: с php + apache, если быть щедрым, это, вероятно, 100 * 15 МБ = 1,5 ГБ (примерно, стреляйте в меня), оперативной памяти прямо здесь. Nginx просто передаст файл ядру, а затем он будет загружен прямо с диска в сетевые буферы. Быстро!
PPS: И с помощью этого метода вы по-прежнему можете выполнять любой контроль доступа и работу с базой данных, которую хотите.
источник
readfile()
Вот чистое решение PHP. Я адаптировал следующую функцию из моего личного фреймворка :
Код настолько эффективен, насколько это возможно, он закрывает обработчик сеанса, так что другие сценарии PHP могут выполняться одновременно для одного и того же пользователя / сеанса. Он также поддерживает загрузку в диапазонах (я подозреваю, что это также то, что Apache делает по умолчанию), так что люди могут приостанавливать / возобновлять загрузки, а также получать выгоду от более высоких скоростей загрузки с помощью ускорителей загрузки. Он также позволяет указать максимальную скорость (в Кбит / с), с которой должна выполняться загрузка (часть), с помощью
$speed
аргумента.источник
eio
также не всегда доступен. Тем не менее, +1 не знал об этом расширении pecl. =)$size = sprintf('%u', filesize($path))
?Пусть Apache сделает всю работу за вас.
источник
Лучшая реализация с поддержкой кеширования и настраиваемыми заголовками http.
источник
если у вас есть возможность добавить расширения PECL в свой php, вы можете просто использовать функции из пакета Fileinfo для определения типа содержимого, а затем отправить правильные заголовки ...
источник
Download
Упомянутая здесь функция PHP вызвала некоторую задержку перед фактической загрузкой файла. Я не знаю, было ли это вызвано использованием кеша лака или чем-то еще, но мне помоглоsleep(1);
полностью удалить его и установить$speed
на1024
. Теперь он работает без проблем, чертовски быстро. Возможно, вы могли бы изменить и эту функцию, потому что я видел, как она используется во всем Интернете.источник
Я написал очень простую функцию для обслуживания файлов с помощью PHP и автоматического определения типа MIME:
использование
источник