最快的方式来使用PHP服务文件 [英] Fastest Way to Serve a File Using PHP

查看:194
本文介绍了最快的方式来使用PHP服务文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图把一个接收文件路径的函数放在一起,标识它是什么,设置合适的头文件,并像Apache一样提供它。



我这样做的原因是因为我需要使用PHP来处理一些有关请求的信息,然后才能提供文件。

速度很关键



虚拟()不是一个选项

必须在共享主机环境中工作,用户无法控制Web服务器(Apache / Nginx等)



这是我到目前为止:

  File :: output($ path); 

php
class文件{
静态函数输出($ path){
//检查文件是否存在
if(!File :: exists($ path)){
header('HTTP / 1.0 404 Not Found');
exit();
}

//设置内容类型头文件
头文件('Content-Type:'.File :: mimeType($ path));
$ b $ //处理缓存
$ fileModificationTime = gmdate('D,d M Y H:i:s',File :: modifyTime($ path))。
$ 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);

//读取文件
readfile($ path);

exit();


静态函数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';
'jpg':
'jpeg':
'jpe':
返回'image / jpg';
'png':
'gif':
'bmp':
'tiff':
返回'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':
'pps':
返回'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':
'aif':
return'audio / aiff';
case'avi':
返回'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);
}
返回未知/。 trim($ fileSuffix [0],'。');
}
}
}
?>


解决方案

记录在案,这里有一个更新,其中包括解决方案的摘要和其他人的讨论。

这些解决方案是从最好的解决方案到最差的也从最需要控制网络服务器的解决方案到需要较少的解决方案。看起来没有一种简单的方法可以让一个解决方案既快速又方便。




使用X-SendFile头文件



正如其他人所记载的那样,这实际上是最好的方法。基本的是,你在php中进行访问控制,然后自己发送文件,而不是通过web服务器来完成。



基本的php代码是: / p>

  header(X-Sendfile:$ file_name); 
header(Content-type:application / octet-stream);
header('Content-Disposition:attachment; filename =''。basename($ file_name)。'');

其中 $ file_name 是完整路径文件系统。

这个解决方案的主要问题是,它需要被Web服务器所允许,并且不是默认安装(apache),isn' t是缺省的活动(lighttpd)或需要一个特定的配置(nginx)。

Apache



你使用mod_php你需要安装一个名为 mod_xsendfile 的模块,然后配置它(如果你在apache配置或.htaccess中允许它)

$ pre $ $ b $ X $ X $ X $ X $ X $ / $ / $ / $ / $ / $ / b

使用此模块,文件路径可以是绝对的或相对于指定的 XSendFilePath < code

$ h $ Light $ p

mod_fastcgi支持这个配置时使用

 allow-x-send-file=> enable

该功能的文档位于 X-LIGHTTPD-send-file lighttpd wiki 。 c>头文件,但 X-Sendfile 名称也可以工作

Nginx



在Nginx中,您不能使用 X-Sendfile 标题,您必须使用自己的名为 X-Accel-Redirect 。它是默认启用的,唯一真正的区别是它的参数应该是一个URI而不是文件系统。其结果是,你必须在你的配置中定义一个标记为内部的位置,以避免客户端找到真正的文件url并直接去找它,他们的wiki包含一个很好的解释

符号链接和位置标题



您可以使用符号链接并重定向到它们,只需使用随机名称创建符号链接到您的文件当用户被授权访问文件并使用以下方式将用户重定向到该文件:

 标题(Location:。$ url_of_symlink ); 

显然你需要一种方法来修剪它们,当创建它们的脚本被调用或者通过cron(在机器上,如果你有访问或通过一些webcron服务,否则)

在Apache下,你需要能够启用 FollowSymLinks .htaccess 或在apache配置中。 / p>

通过IP和位置标题访问控制



另一个窍门是从php生成apache访问文件, IP。在apache下,它意味着使用 mod_authz_host mod_access 允许来自命令



问题在于,锁定对文件的访问权限(因为多个用户可能希望同时执行此操作)不是微不足道的,可能会导致一些用户长时间等待。而且你仍然需要修剪文件。

显然,另一个问题是,同一个IP后面的多个人可能会访问这个文件。



当一切都失败的时候如果你真的没有办法让你的web服务器来帮助你,唯一的解决办法是阅读文件在当前正在使用的所有php版本中都可用,并且工作得很好(但是不是






合并解决方案



很好,如果你希望你的PHP代码能够在任何地方都可用,发送文件的最好方法是在某个地方有一个可配置的选项,并提供关于如何根据Web服务器来激活它的说明,或者在你的安装脚本中自动检测。




    li>清理网址( mod在$ apache上的_rewrite
  • 加密函数( mcrypt php模块)
  • 多字节字串支援( mbstring php模组)

I'm trying to put together a function that receives a file path, identifies what it is, sets the appropriate headers, and serves it just like Apache would.

The reason I am doing this is because I need to use PHP to process some information about the request before serving the file.

Speed is critical

virtual() isn't an option

Must work in a shared hosting environment where the user has no control of the web server (Apache/nginx, etc)

Here's what I've got so far:

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], '.');
    }
}
}
?>

解决方案

My previous answer was partial and not well documented, here is an update with a summary of the solutions from it and from others in the discussion.

The solutions are ordered from best solution to worst but also from the solution needing the most control over the web server to the one needing the less. There don't seem to be an easy way to have one solution that is both fast and work everywhere.


Using the X-SendFile header

As documented by others it's actually the best way. The basis is that you do your access control in php and then instead of sending the file yourself you tell the web server to do it.

The basic php code is :

header("X-Sendfile: $file_name");
header("Content-type: application/octet-stream");
header('Content-Disposition: attachment; filename="' . basename($file_name) . '"');

Where $file_name is the full path on the file system.

The main problem with this solution is that it need to be allowed by the web server and either isn't installed by default (apache), isn't active by default (lighttpd) or need a specific configuration (nginx).

Apache

Under apache if you use mod_php you need to install a module called mod_xsendfile then configure it (either in apache config or .htaccess if you allow it)

XSendFile on
XSendFilePath /home/www/example.com/htdocs/files/

With this module the file path could either be absolute or relative to the specified XSendFilePath.

Lighttpd

The mod_fastcgi support this when configured with

"allow-x-send-file" => "enable" 

The documentation for the feature is on the lighttpd wiki they document the X-LIGHTTPD-send-file header but the X-Sendfile name also work

Nginx

On Nginx you can't use the X-Sendfile header you must use their own header that is named X-Accel-Redirect. It is enabled by default and the only real difference is that it's argument should be an URI not a file system. The consequence is that you must define a location marked as internal in your configuration to avoid clients finding the real file url and going directly to it, their wiki contains a good explanation of this.

Symlinks and Location header

You could use symlinks and redirect to them, just create symlinks to your file with random names when an user is authorized to access a file and redirect the user to it using:

header("Location: " . $url_of_symlink);

Obviously you'll need a way to prune them either when the script to create them is called or via cron (on the machine if you have access or via some webcron service otherwise)

Under apache you need to be able to enable FollowSymLinks in a .htaccess or in the apache config.

Access control by IP and Location header

Another hack is to generate apache access files from php allowing the explicit user IP. Under apache it mean using mod_authz_host (mod_access) Allow from commands.

The problem is that locking access to the file (as multiple users may want to do this at the same time) is non trivial and could lead to some users waiting a long time. And you still need to prune the file anyway.

Obviously another problem would be that multiple people behind the same IP could potentially access the file.

When everything else fail

If you really don't have any way to get your web server to help you, the only solution remaining is readfile it's available in all php versions currently in use and work pretty well (but isn't really efficient).


Combining solutions

In fine, the best way to send a file really fast if you want your php code to be usable everywhere is to have a configurable option somewhere, with instructions on how to activate it depending on the web server and maybe an auto detection in your install script.

It is pretty similar to what is done in a lot of software for

  • Clean urls (mod_rewrite on apache)
  • Crypto functions (mcrypt php module)
  • Multibyte string support (mbstring php module)

这篇关于最快的方式来使用PHP服务文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆