使用 PHP 提供文件的最快方法 [英] Fastest Way to Serve a File Using PHP

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

问题描述

我正在尝试组合一个函数,该函数接收文件路径、识别它是什么、设置适当的标头并像 Apache 一样提供它.

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.

我这样做的原因是因为我需要在提供文件之前使用 PHP 来处理有关请求的一些信息.

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

速度至关重要

virtual() 不是一种选择

必须在用户无法控制网络服务器(Apache/nginx 等)的共享托管环境中工作

这是我到目前为止所得到的:

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

推荐答案

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

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

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.

正如其他人所记录的那样,这实际上是最好的方法.基础是您在 php 中进行访问控制,然后您不是自己发送文件,而是告诉 Web 服务器来执行此操作.

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.

基本的php代码是:

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

其中 $file_name 是文件系统上的完整路径.

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

这个解决方案的主要问题是它需要被 web 服务器允许,并且默认情况下没有安装 (apache),默认情况下不活动 (lighttpd) 或需要特定配置 (nginx).

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 下,如果您使用 mod_php,则需要安装一个名为 mod_xsendfile 的模块,然后对其进行配置(在 apache 中)config 或 .htaccess(如果您允许)

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/

有了这个模块,文件路径可以是绝对的,也可以是相对于指定的XSendFilePath.

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

mod_fastcgi 在配置时支持这个

The mod_fastcgi support this when configured with

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

该功能的文档位于 lighttpd wiki他们记录了 X-LIGHTTPD-send-file 标头,但 X-Sendfile 名称也有效

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

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.

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

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);

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

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)

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

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

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

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.

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

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

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

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).

好吧,如果你想让你的 php 代码在任何地方都可用,那么真正快速地发送文件的最好方法是在某处有一个可配置的选项,并说明如何根据网络服务器激活它,也许还有自动检测在您的安装脚本中.

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

  • 清理网址(mod_rewrite on apache)
  • 加密函数(mcrypt php 模块)
  • 多字节字符串支持(mbstring php 模块)
  • Clean urls (mod_rewrite on apache)
  • Crypto functions (mcrypt php module)
  • Multibyte string support (mbstring php module)

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

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