菜单

Sending files better: Apache mod_xsendfile and PHP

2013年07月16日 - apache

I have previously written a quick post on making files downloadable through PHP scripts. The example in the post reads the file itself into a variable, and as pointed out in the comments, it’s not necessarily a very good idea especially if you deal with large files.

Recently at work, we needed a reliable way to send files to users’ browser, and I decided to take a look at mod_xsendfile, as suggested by Tom Graham.

It’s also supported by other web servers such as Lighttpd and nginx.

Benefits of xsendfile over other methods

The advantages of xsendfile come from dealing with bigger files. If you try reading a big file into memory for sending through PHP, or otherwise send one through PHP, you may hit various roadblocks:

If you don’t encounter these two, you may find that sending the file through the PHP process is slower, or it may eat more memory from your server than if it was sent by Apache.

mod_xsendfile solves all these problems.

Setting up mod_xsendfile

Since mod_xsendfile is not a standard Apache module, you will need to compile and install it yourself. This may be a problem on some hosting providers who won’t let you do this / refuse to install it for you!

Installing the module is quite simple:

You now have the module installed. To use it, you will need to add the following settings to your Apache configuration, or to a .htaccess file:

# enable xsendfile
XSendFile On

# enable sending files from parent dirs
XSendFileAllowAbove On

The second one is optional, but it allows a useful behavior. You may want to store your protected files under the web root, so to allow xsendfile to access the files, you will need to enable XSendFileAllowAbove – otherwise you will only be able to access files that are in the same directory or in child directories of the directory, where the parsing script is.

Sending files

Sending a file with xsendfile is very straightforward:

<?php
//We want to force a download box with the filename hello.txt
header('Content-Disposition: attachment;filename=hello.txt');

//File is located at /home/username/hello.txt
header('X-Sendfile: /home/username/hello.txt');

You could omit the first header, in which case the browser would not necessarily show a download file dialog. Also, the X-Sendfile header will not show up in the user’s browser, and they will never see the real location of the file they received.

You will not need to send a Content-Length header, as Apache will take care of that for you.

In closing

There are multiple ways to send and store files. I think using mod_xsendfile is a quite good approach, as it avoids the problems associated with sending the files in a PHP script. It also allows Apache to do it’s job – sending files to users – while still giving you the ability to, for example, perform authentication using a PHP script.

Support in other web servers

For information on how to use x-sendfile with Lighttpd, see this comment by Kawsar Saiyeed.

For information on using x-sendfile with nginx, see this comment by Alexey Shockov

Relation: http://blog.csdn.net/21aspnet/article/details/7750530

发表评论

电子邮件地址不会被公开。 必填项已用*标注