使用PHP提供文件的最快方法是什么?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (59)

我试图把一个接收文件路径的函数放在一起,标识它是什么,设置适当的头文件,并像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], '.');
    }
}
}
?>
提问于
用户回答回答于

使用X-SendFile标头

正如其他人所记载的那样,这实际上是最好的方式 基础是,你在php中进行访问控制,然后不要自己发送文件,而是告诉Web服务器去做。

基本的PHP代码是:

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

$file_name文件系统上的完整路径在哪里?

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

Apache

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

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

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

Lighttpd的

mod_fastcgi在配置时支持

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

该功能的文档位于lighttpd wiki上,它们记录了X-LIGHTTPD-send-file标题,但X-Sendfile名称也适用

Nginx的

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

符号链接和位置标题

可以使用符号链接并重定向到它们,只需在用户有权访问文件并使用以下命令将用户重定向到文件时,使用随机名称创建符号链接:

header("Location: " . $url_of_symlink);

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

在Apache你需要能够使FollowSymLinks.htaccess或Apache的配置。

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

另一个黑客是从PHP生成Apache访问文件,允许明确的用户IP。在Apache下它意味着使用mod_authz_hostmod_accessAllow from命令。

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

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

当一切都失败时

如果你真的没有办法让你的web服务器来帮助你,唯一剩下的解决方案是readfile,它可以在当前使用的所有php版本中使用,并且工作得很好(但效率不高)。

结合解决方案

总而言之,如果您希望您的php代码在任何地方都可用,发送文件的最佳方式是在某处配置一个可配置选项,并提供关于如何激活它的说明,具体取决于Web服务器,也可能是安装时的自动检测脚本。

这与许多软件所做的很相似

  • 清理网址(mod_rewrite在Apache上)
  • 加密功能(mcryptphp模块)
  • 多字节字符串支持(mbstringphp模块)
用户回答回答于

最快的方法:不要。查看nginx的x-sendfile标头,其他web服务器也有类似的东西。这意味着你仍然可以在php中进行访问控制等,但将文件的实际发送委托给为此设计的Web服务器。

扫码关注云+社区