Loading...

which is better : readfile, fpassthru, or header

有这样一个需求摆在眼前:

  1. web服务器上有很多文件要提供给用户下载;
  2. 必须隐藏文件真实的url,防止盗链和未授权的下载;
  3. 不要太占用CPU资源

于是有朋友提出解决方法如下(假设代码在download.php文件中):

  1. 直接header(“location:src”);
  2. 通过php readfile
  3. 采用fpassthru读取文件然后输出

当然,我们知道,采用header是无法加密文件真实路径的,在某些客户端中,真实的路径会被完全重定向输出的,所以第一个方案无效。第二个和第三个方案是可行的,我们完全可以通过.htaccess控制访问文件所在的目录,所以对于保密和授权的需求来说是完全满足的,但是有一个问题,这两种方案会很占用CPU资源么?特别是读取上百兆的文件的时候?

Readfile_vs_include告诉我们,include文件和readfile对于内存和CPU时间的占用没有丝毫差别,所以采用readfile是可行的,下面引用的表格为证:

Function Sample Usage Time (s) Memory (b)
file_get_contents echo file_get_contents($filename); 0.00564 1067856
fpassthru fpassthru($fp); 0.00184 20032
fgets
$fp = fopen($filename,"rb");
while(!feof($fp))
{
    echo fgets($fp);
}
0.07190 30768
file echo join(“”,file($filename)); 0.06464 2185624
require_once require_once($filename); 0.08065 2067696
include include($filename); 0.08202 2067696
readfile readfile($filename); 0.00191 19208

另外,对于超大文件,在readfile的时候建议不要一次性读取,否则内存消耗极大。然后根据cosbeta的实验发现采用readfile一次性读取,内存消耗会适量增加,但是CPU的利用率会下降较多。如果采用分段读取的方式,内存会稍微下降,而CPU占用却会明显上升,分段读取的示例如下:

//readfile($filelocation);
$file = fopen($filelocation);
while (!feof($file)){
//Last I heard, 2048 optimized some PHP internal buffer…
//That’s probably way out of date!
//And does not account for your bandwidth bottle-neck anyway.
//Play with the 2048 until you’re happy.
echo fread($file, 2048);
}

另外,有人开发了一个mod_xsendfile(墙外),这个东西比上面所有的方案都要好,前提条件是你的虚拟主机已经安装这个模块。下面是这个模块的部分介绍:

mod_xsendfile is a small Apache2 module that processes X-SENDFILE headers registered by the original output handler.

If it encounters the presence of such header it will discard all output and send the file specified by that header instead using Apache internals including all optimizations like caching-headers and sendfile or mmap if configured. If it encounters the presence of such header it will discard all output and send the file specified by that header instead using Apache internals including all optimizations like caching-headers and sendfile or mmap if configured.

It is useful for processing script-output of eg php, perl or any cgi. It is useful for processing script-output of eg php, perl or any cgi.

它的好处在于:

# Some applications require checking for special privileges.用在权限控制文件读取的地方
# Other have to lookup values first (eg. from a DB) in order to correctly process a download request.在下载之前需要做一些预处理的地方 Other have to lookup values first (eg. from a DB) in order to correctly process a download request.
# Or store values (download-counters come into mind). Or store values (download-counters come into mind).
# etc.

代码示例(你也可以通过这个代码来检查你当前的服务器是否已经安装该模块):

header ( “X-Sendfile: $somefile” );
header ( “Content-Type: application/octet-stream” );
header ( “Content-Disposition: attachment; file=\”$somefile\”” );
exit;

废话到此结束,cosbeta想表达的意思就是,如果要用php下载大文件(也不要太大),尽情的采用readfile吧,当然,如果支持mod_xsendfile,则毫不犹豫的优先采用它。

标签:
发表于 2008-11-14 17:08:16 目录:PHP, 电子技术, 网站技术 [RSS 2.0] 你可以发表评论, 或者从您的网站 trackback
  • 相关阅读
  • homezz 美国专业主机商
    已经有7位大师动手指导 拒绝低俗
    • 1楼 Jor 在2008.11.14 18:20发表评论如下: 回复

      readfile很好,哈哈!!!

      • 2楼 NickyYe 在2008.11.14 18:52发表评论如下: 回复

        好文章,多谢

        • 3楼 epile 在2008.11.14 19:36发表评论如下: 回复

          江东兄明天去年会吗?

          • 3楼附属品 江东 在2008.11.14 20:05发表评论如下: 回复

            明天要考驾照,后天看去不去吧

          • 4楼 blankyao 在2008.11.14 22:05发表评论如下: 回复

            从前都是用第一个方案,上传的时候把文件名存到数据库中,然后把取个随机的文件名,当下载的时候再提出来文件名。
            现在看来用readfile更好啊 🙂

            • 5楼 blankyao 在2008.11.14 22:10发表评论如下: 回复

              你这代码是用的什么插件啊?连高亮都没有…

              • 5楼附属品 江东 在2008.11.15 12:42发表评论如下: 回复

                是你太性急,不等js渲染而已,其实有高亮的插件阿

              评论分页: 1
              (Required)
              (Required, not published)
              如果留言未显示无需重复留言,我将为你恢复!