Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Serve large file with PHP and nginx X-Accel-Redirect

Tags:

php

nginx

Hi I want user to be able to download PDF file from my server (Windows) configured with nginx PHP. This is my nginx.conf (server block)

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile      on;
    keepalive_timeout  65;
    server {
        listen       80;
        server_name  localhost;
        location / {
            root   html;
            index  index.php;

        }

         location /download {
            internal;
            alias /protected;
        }
    }
}

..and the PHP file (header part)

$file = '/download/real-pdf-file.pdf'; //this is the physical file path
$filename = 'user-pdf-file.pdf'; //this is the file name user will get
header('Cache-Control: public, must-revalidate');
header('Pragma: no-cache');
header('Content-Type: application\pdf');
header('Content-Length: ' .(string)(filesize($file)) );
header('Content-Disposition: attachment; filename='.$filename.'');
header('Content-Transfer-Encoding: binary');
header('X-Accel-Redirect: '. $file);

The file is call from URL like this:

download.php?id=a2a9ca5bd4a84294b421fdd9ef5e438ded7fcb09

I've try few example/solution from here but so far none is working. The file is large (between 250 to 400 MB each) and each user will be able to download 4 files.

There is no issue with downloading part using PHP, only the nginx configuration that seem not working. No error log detected.

like image 454
Fadli Saad Avatar asked Apr 24 '13 10:04

Fadli Saad


2 Answers

Ok - there's a couple of issues here:

1) Puting root inside of a location is a BAD IDEA according to the nginx developers.

2) The internal URL used for telling Nginx that this is an internal redirect shouldn't be exposed to users.

3) I can't see where your download.php file is being served from, so I changed your root location block to use try_files so a request to /download.php would be served by that file rather than index.php.

Your project should be laid out as:

project\
    html - this is the root of your website
    protected  - this directory is not accessible directly

And your Nginx conf should look like:

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile      on;
    keepalive_timeout  65;
    server {
        listen       80;
        server_name  localhost;

        root   /path/to/project/html;

        location / {
            try_files $uri /index.php?$args;
        }

        location /protected_files {
            internal;
            alias /path/to/project/protected;
        }
    }
}

It's a good idea not to re-use the same names to mean different things as it's quite confusing. I've changed them so that now protected just refers to the actual physical directory that holds the files you want to serve. protected_files is just a string that allows Nginx to match the request from the x-accel header.

The only thing that needs changing in your PHP code is to use the correct string to allow Nginx to pickup the internal location:

$aliasedFile = '/download/real-pdf-file.pdf'; //this is the nginx alias of the file path
$realFile = '/path/to/project/protected/real-pdf-file.pdf'; //this is the physical file path
$filename = 'user-pdf-file.pdf'; //this is the file name user will get
header('Cache-Control: public, must-revalidate');
header('Pragma: no-cache');
header('Content-Type: application\pdf');
header('Content-Length: ' .(string)(filesize($realFile)) );
header('Content-Disposition: attachment; filename='.$filename.'');
header('Content-Transfer-Encoding: binary');
header('X-Accel-Redirect: '. $aliasedFile);
exit(0);
like image 189
Danack Avatar answered Oct 21 '22 12:10

Danack


Based from recommendation and solution from Danack and some clarification from Carsten, I found that in Windows Serve, we need to set the full path in the alias like this:

location /protected_files {
            internal;
            alias C:/absolute/path/to/project/protected/;
        }

Please note that the extra forward slash is needed (in my case, Windows 7 Pro on development, Windows Server 2008 for deployment). The only issue is now I need to test concurrent downloads to see if server resources is hogging.

I'm new with nginx, since it seem really faster that I switch from Apache. Thanks guy for the enlightenment! I'am glad to be part of Stackoverflow community :)

like image 34
Fadli Saad Avatar answered Oct 21 '22 11:10

Fadli Saad