我试图将一个接收文件路径的函数放在一起,确定它是什么,设置适当的标头,然后像Apache一样提供它。
之所以这样做,是因为在提供文件之前,我需要使用PHP处理有关请求的某些信息。
速度很关键
virtual()不可选项
必须在用户无法控制Web服务器(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], '.'); } } } ?>
我正在尝试将一个函数接收文件路径,确定它是什么,设置适当的标头,并像Apache一样提供它。我这样做的原因是因为我...
我以前的回答是不完整的,没有得到充分的记录,这里是更新,其中总结了该解决方案以及讨论中的其他解决方案。
最快的方法:不要。查看mod_rewrite
,其他Web服务器也有类似的内容。这意味着您仍然可以在php中进行访问控制等,但是将文件的实际发送委托给为此设计的Web服务器。
P.S:我不寒而栗,只是想想与在php中读取和发送文件相比,将它与nginx一起使用效率更高。试想一下,如果有100个人正在下载文件:使用php + apache,那么慷慨,那大概是100 * 15mb = 1.5GB(大约,给我射击),就在那儿。 Nginx只会将文件发送到内核,然后将其直接从磁盘加载到网络缓冲区中。快速!
P.P.S:而且,通过这种方法,您仍然可以执行所有访问控制所需的数据库工作。
这里有一个纯PHP解决方案。我已经适应了以下功能mcrypt
:
mbstring
该代码效率极高,它会关闭会话处理程序,以便其他PHP脚本可以为同一用户/会话同时运行。它还支持在一定范围内提供下载服务(我怀疑这也是Apache的默认做法),因此人们可以暂停/恢复下载,还可以通过下载加速器受益于更高的下载速度。它还允许您通过x-sendfile header for nginx参数指定下载文件(部分)的最大速度(以Kbps为单位)。
一个更好的实现,具有缓存支持,自定义的HTTP标头。
如果您有可能向您的PHP添加PECL扩展名,则可以简单地使用$speed
中的函数来确定内容类型,然后发送适当的标头...
此处提到的PHP header('Location: ' . $path);
exit(0);
函数在实际开始下载文件之前造成了一些延迟。我不知道这是由于使用清漆缓存引起的还是由什么引起的,但是对我来说,这有助于完全删除serveStaticFile($fn, array(
'headers'=>array(
'Content-Type' => 'image/x-icon',
'Cache-Control' => 'public, max-age=604800',
'Expires' => gmdate("D, d M Y H:i:s", time() + 30 * 86400) . " GMT",
)
));
function serveStaticFile($path, $options = array()) {
$path = realpath($path);
if (is_file($path)) {
if(session_id())
session_write_close();
header_remove();
set_time_limit(0);
$size = filesize($path);
$lastModifiedTime = filemtime($path);
$fp = @fopen($path, 'rb');
$range = array(0, $size - 1);
header('Last-Modified: ' . gmdate("D, d M Y H:i:s", $lastModifiedTime)." GMT");
if (( ! empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $lastModifiedTime ) ) {
header("HTTP/1.1 304 Not Modified", true, 304);
return true;
}
if (isset($_SERVER['HTTP_RANGE'])) {
//$valid = preg_match('^bytes=\d*-\d*(,\d*-\d*)*$', $_SERVER['HTTP_RANGE']);
if(substr($_SERVER['HTTP_RANGE'], 0, 6) != 'bytes=') {
header('HTTP/1.1 416 Requested Range Not Satisfiable', true, 416);
header('Content-Range: bytes */' . $size); // Required in 416.
return false;
}
$ranges = explode(',', substr($_SERVER['HTTP_RANGE'], 6));
$range = explode('-', $ranges[0]); // to do: only support the first range now.
if ($range[0] === '') $range[0] = 0;
if ($range[1] === '') $range[1] = $size - 1;
if (($range[0] >= 0) && ($range[1] <= $size - 1) && ($range[0] <= $range[1])) {
header('HTTP/1.1 206 Partial Content', true, 206);
header('Content-Range: bytes ' . sprintf('%u-%u/%u', $range[0], $range[1], $size));
}
else {
header('HTTP/1.1 416 Requested Range Not Satisfiable', true, 416);
header('Content-Range: bytes */' . $size);
return false;
}
}
$contentLength = $range[1] - $range[0] + 1;
//header('Content-Disposition: attachment; filename="xxxxx"');
$headers = array(
'Accept-Ranges' => 'bytes',
'Content-Length' => $contentLength,
'Content-Type' => 'application/octet-stream',
);
if(!empty($options['headers'])) {
$headers = array_merge($headers, $options['headers']);
}
foreach($headers as $k=>$v) {
header("$k: $v", true);
}
if ($range[0] > 0) {
fseek($fp, $range[0]);
}
$sentSize = 0;
while (!feof($fp) && (connection_status() === CONNECTION_NORMAL)) {
$readingSize = $contentLength - $sentSize;
$readingSize = min($readingSize, 512 * 1024);
if($readingSize <= 0) break;
$data = fread($fp, $readingSize);
if(!$data) break;
$sentSize += strlen($data);
echo $data;
flush();
}
fclose($fp);
return true;
}
else {
header('HTTP/1.1 404 Not Found', true, 404);
return false;
}
}
并将Fileinfo package设置为Download
。现在,它可以像地狱般快速地工作,而没有任何问题。也许您也可以修改该功能,因为我看到它在整个Internet上都使用过。
我编写了一个非常简单的函数,可以通过PHP和自动MIME类型检测来提供文件:
sleep(1);