php - 使用 PHP 提供文件的最快方法

标签 php performance file-io x-sendfile

我正在尝试组合一个函数,该函数接收文件路径、识别它是什么、设置适当的 header 并像 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], '.');
    }
}
}
?>

最佳答案

我之前的回答是部分的并且没有很好的记录,这里是一个更新,其中包含来自它和讨论中其他人的解决方案的摘要。

解决方案从最好的解决方案到最差的解决方案排序,但也从需要对 Web 服务器进行最多控制的解决方案到需要较少的解决方案。似乎没有一种简单的方法可以让一个解决方案既快速又适用于任何地方。

使用 X-SendFile header

正如其他人所记录的那样,这实际上是最好的方法。基础是您在 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 .

轻量级

mod_fastcgi 在配置时支持这个
"allow-x-send-file" => "enable" 

该功能的文档位于 lighttpd wiki 上他们记录了 X-LIGHTTPD-send-file标题但 X-Sendfile名字也有用

nginx

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

符号链接(symbolic link)和位置标题

您可以使用 symlinks并重定向到它们,当用户被授权访问文件并使用以下命令将用户重定向到该文件时,只需使用随机名称创建指向文件的符号链接(symbolic link):
header("Location: " . $url_of_symlink);

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

在 apache 下你需要能够启用 FollowSymLinks .htaccess 或在 apache 配置中。

通过 IP 和 Location header 进行访问控制

另一个黑客是从允许显式用户 IP 的 php 生成 apache 访问文件。在 apache 下,这意味着使用 mod_authz_host ( mod_access ) Allow from命令。

问题在于锁定对文件的访问(因为多个用户可能希望同时执行此操作)并非易事,并且可能导致某些用户等待很长时间。无论如何,您仍然需要修剪文件。

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

当其他一切都失败时

如果你真的没有任何办法让你的网络服务器来帮助你,剩下的唯一解决方案是 readfile它在当前使用的所有 php 版本中都可用,并且运行良好(但效率不高)。

组合解决方案

好吧,如果您希望您的 php 代码在任何地方都可用,那么真正快速发送文件的最佳方法是在某处有一个可配置的选项,并附有有关如何根据 Web 服务器激活它的说明,并且可能在您的安装中进行自动检测脚本。

它与许多软件中所做的非常相似
  • 清理网址( mod_rewrite 在 apache 上)
  • 加密函数(mcrypt php 模块)
  • 多字节字符串支持(mbstring php 模块)
  • 关于php - 使用 PHP 提供文件的最快方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3697748/

    相关文章:

    php - 更改 Wordpress 主题中的 "Load More"按钮的最佳方法是什么?

    c# - 增强以下组合算法的性能

    c# - 关于字符串实习表现的问题

    c - 将 int 数组快速转换为 fp 以进行音频处理

    Python-从函数中的文本文件中读取数字

    php - SQLSTATE[HY000] [2002] :schema:create 时不是目录

    php - 查找具有特殊字符的字符串的最后一个单词

    java - 用于存储来自文件输入 Java 的动态大小的 block 的最佳数据结构

    c++ - 如何创建新文件夹并将文件保存到其中?