Wed 1 Nov 2006
Использование X-Accel-Redirect с Nginx для реализации контролируемых скачиваний (с примерами для rails и php)
Posted by Scoundrel under Development ·
Иногда вам может быть нужно реализовать т.н. контролируемое скачивание, когда все запросы на скачивание файлов передаются скрипту, который решает, как поступить: отправить пользователю какой-либо файл, или показать стриницу access denied, или, может быть, сделать что-то еще. При использовании сервера lighttpd это может быть реализовано при помощи заголовка X-Sendfile, возвращаемого из скрипта. Nginx имеет свою союственную реализацию описанной идеи с использованием заголовка X-Accel-Redirect. В этой короткой статье я попытаюсь описать, как использовать эту возможность из приложений на PHP или Rails.
Представим, что у вас есть какой-либо сайт, работающий на Apache с PHP или Rails для генерации нинамического контента. Если вы будете использовать nginx в качестве reverse-proxy перед вашим сервером Apache, вы получите сразу две положительных возможности:
- Вы сможете освободить больше ресурсов вашего сервера для обслуживания клиентов, т.к. nginx возьмет на себя работу с медленными клиентами (детальнее - здесь).
- Вы сможете реализовать контролируемое скачивание статических файлов с вашего сайта.
В этой статье я предпологаю, что сайт расположен в каталоге /var/www и статические файлы (например, фильмы, музыка или что-то еще) расположены в каталоге /var/www/files. Apache слушает на порту http://127.0.0.1:8080.
Для начала, давайте рассмотрим нашу конфигурацию сервера nginx:
....
server {
listen 80;
server_name your-domain.com;
location / {
rewrite ^/download/(.*) /down.php?path=$1 last;
proxy_pass http://127.0.0.1:8080/;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_buffer_size 4k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
}
location /files {
root /var/www;
internal;
}
}
}
Как вы видите, у нас есть дополнительная “internal” секция location /files. Это ключевое влово “internal” позволяет нам иметь секции location, которые будут доступны для польщователя только в случае внутренних редиректов внутри nginx и при использование заголовка X-Accel-Redirect в ответах от сриптов backend-сервера. Итак, мы можем использовать простой скрипт на PHP или код на Rails для реализации контролируемых скачиваний с поддержкой заголовков Ranges (докачка) и всех остальных возможностей, предоставляемых при прямом скачивании статического контента с серверов под управлением nginx.
Вот пример очень простого скрипта down.php:
// Get requested file name
$path = $_GET["path"];
//...
// Perform any required security checks, validation
// and/or stats accounting
//...
// And redirect user to internal location
header("X-Accel-Redirect: /files/" . $path);
?>
В приложениях Rails вы можете использовать следубщий код в вашем controller’е:
path = @params["path"]
# ...
# Perform any required security checks, validation
# and/or stats accounting
# ...
# And redirect user to internal location
@response.headers['X-Accel-Redirect'] = "/files/" + path
Вот и все! При помощи описанного подхода вы сможете реализовать очень гибкую и удивительно эффективную систему раздачи любого статического контента!
- Using Nginx, SSI and Memcache to Make Your Web Applications Faster
- 32bit VS 64bit - what do you use?
- HAProxy - Надежный, высокопроизводительный балансировщик нагрузки для TCP/HTTP
- Compiling nginx in RedHat Linux: PCRE library problem
- Использование Nginx Как Reverse-Proxy Сервера На Загруженных Сайтах
November 1st, 2006 at 9:55 am
cool article
I have add it to the wiki site http://wiki.codemongers.com/NginxXSendfile
November 7th, 2006 at 6:31 am
Класс!
December 28th, 2006 at 7:49 pm
Will it work if you want to use the flv module with start=XXX parameter?
December 28th, 2006 at 8:55 pm
2Rapsey: Why not?
December 29th, 2006 at 7:20 am
because it would be to awesome.
March 4th, 2007 at 8:08 am
привет!
я реализовал контролируемые скачивания, но есть одна проблема - nginx со всеми файлами передает заголовок text/html, и браузер его соответственно открывает. не подскажете, с чем это может быть связано, и как испраить?
March 27th, 2007 at 9:08 pm
$r->send_http_header posulaet eto,
4to-bu zapretit - powli pustoj $r->send_http_header();
March 28th, 2007 at 4:04 am
да я затупил, короче… там ошибка была, типа, нет такого темплейта. она и заменяла заголовок на text/html, а тело документа менялось на содержимое файла.
April 5th, 2007 at 8:41 am
[...] 原文链接:Using X-Accel-Redirect Header With Nginx to Implement Controlled Downloads (with rails and php examples) [...]
April 5th, 2007 at 9:11 am
good article.
I have thanslated it into Simplified-Chinese:
http://bianbian.sunshow.net/index.php/technology/154.html
enjoy~
April 23rd, 2007 at 8:34 am
Неужели и не решили проблеммы с сменой заголовка ??
May 16th, 2007 at 4:35 pm
Здорово. Часто под контролируемым скачиванием понимают еще и отметку, когда скачивание файла было закончено. Тут nginx помочь может?
May 16th, 2007 at 6:18 pm
2pereksid: Можно и такое. Называлось вроде post_action - нгинкс умеет дергать нужный урл когда юзер все докачает.
June 20th, 2007 at 3:35 pm
Можно по подробнее, из-за чего nginx выдает на .exe файлы, text/html вместо application/octet-stream?
June 26th, 2007 at 4:21 pm
> Можно по подробнее, из-за чего nginx выдает на .exe файлы, text/html вместо application/octet-stream?
Это может быть потому, что процесс выдает ошибку “500 No template for action download”
так как ошибка выводится в html, то она подменяет заголовок на text/html.
проверьте логи ruby
July 7th, 2007 at 11:34 pm
DOES NGINX IPV6 CAPABLE ???????!!!!!!!!!!!!!??????????????????!!!!!!!!!!!!!???????????????????!!!!!!!!!!!!!!!!!!!!
July 13th, 2007 at 12:51 pm
нет, это статья не все. если использовать пхп как модуль апача, то сервер ляжет под нагрузкой, когда обратятся несколько юзверов при помощи флашгета с 10 потоками. Лучшим вариантом будет запуск проверяющего скрипта на фастCGI.
July 15th, 2007 at 8:30 pm
Как вы смотрите на то, чтобы для уменьшения нагрузки дописать к nginx модуль или фильтр какой нить для реализации антилича.
July 18th, 2007 at 8:22 pm
2si-rus: он реализуется родными средствами (if + regexp) в конфиге.
July 31st, 2007 at 2:35 am
[...] Using X-Accel-Redirect Header With Nginx (tags: rails nginx x-accel-redirect) [...]
August 14th, 2007 at 3:10 pm
[...] Understanding FastCGI Application Performance Squid: Accelerator Mode XCache Faster Is Possible Using X-Accel-Redirect Header With Nginx to Implement Controlled Downloads Lighttpd X-Sendfile Scaling with MySQL replication Может чего и забыл - у меня [...]
October 30th, 2007 at 3:43 am
Ошибочка маленькая в rewrite, должно быть так, видимо:
rewrite ^/download/(.*)$ /down.php?path=$1 last;
2 Andrey Popov:
Попробуй перед редиректом (X-Accel-Redirect) отдавать заголовок руками т.е.:
Content-type: application/octet-stream
November 8th, 2007 at 7:51 am
What about the rest of the code for the rails controller.
This confuses me because you have to render something….?
I have nginx setup and I am setting the header, but then what? render :text => “”???
November 9th, 2007 at 10:59 am
For explorer 6 users downloading pdfs you also need to set the cache-control header, or you get a ‘document not found’ error message from the acrobat reader. It seems this is because the explorer regards the file as temporary and deletes it before acrobat can see it.
anyway, i use this to make pdf downloads work:
Otherwise, I’ve been very happy with nginx and this download control mechanism (and I’ve written a radiant plugin to make use of it, which should appear soon). thank you.
November 9th, 2007 at 6:54 pm
a sto tut delat???
November 12th, 2007 at 1:00 pm
[...] X-Accel-Redirect с Nginx для реализации контролируемых [...]
November 13th, 2007 at 10:23 pm
[...] so you too can send files, flowers and love without crashing mongrels with nginx’s equivalent X-Accel-Redirect [...]
November 13th, 2007 at 10:31 pm
This works great for me. I modified the x_send_file rails plugin to work with nginx.
http://spongetech.wordpress.com/2007/11/13/the-complete-nginx-solution-to-sending-flowers-and-files-with-rails/
November 24th, 2007 at 9:28 pm
При таком подходе в заголовке Content-Type всегда оказывается text/html. Соответственно, бинарный файл загружается браузером в окно а не предлагается для скачивания.
Как оказалось, это заголовок по умолчанию, который выдает сам PHP скрипт, и nginx его уже не трогает.
Выход довольно прост, надо запретить PHP выдавать этот заголовок, например, так:
ini_set(’default_mimetype’, ”);
Если кто подскажет, как это сделать более элегантно - буду благодарен.
December 26th, 2007 at 9:37 am
перед X-Accel-Redirect вставьте:
header(”Content-Disposition: attachment; filename=\”" . $filename . “\”");
где $filename - имя файла (только имя, путь - отсутствует).
После этого все браузеры должны открывать окно с приглашением сохранить файл или открыть. Ну у MIME типы уже сам nginx отдаст если в его конфиге добавлено include conf/mime.types
January 15th, 2008 at 11:20 am
Добрый день. А никто не сталкивался со следующей ситуацией: есть файл который отдается через X-Accel-Redirect. При попытке его скачать с двух различных машин - качается только на одной. Проблема имеет место только при скачивании через браузер (тестировался ie6-7 и ff2), выдается невозможность отобразить страницу. При использовании менеджера закачек - все качается нормально.
January 15th, 2008 at 2:37 pm
в тему предыдущего коммента - вопрос закрыт. проблема была в размере файла - больше 4 гб. Интернет експлорер побоялся что на машине фат32 и не стал качать.
January 21st, 2008 at 9:44 am
При попытке использовать описанную схему контролируемого скачивания (nginx+apache+php) столкнулся со след. проблемой: иногда вместо предложения сохранить файл в браузер выдается ошибка :
502 Bad Gateway
nginx/0.5.33
При этом в PHP скрипт передается $path с запорченным окончанием - последние несколько символов заменены на 0×0. В error.log nginx’а при этом выдается сообщение:
upstream prematurely closed connection while reading response header from
upstream …
Как с этим бороться?
January 25th, 2008 at 3:40 pm
>What about the rest of the code for the rails controller.
>This confuses me because you have to render something….?
>I have nginx setup and I am setting the header, but then what? >render :text => “”???
render :nothing => true
February 21st, 2008 at 1:09 pm
[...] 原文链接:Using X-Accel-Redirect Header With Nginx to Implement Controlled Downloads (with rails and php examp… [...]
March 28th, 2008 at 6:14 am
[...] делает X-Accel-Redirect. Подробнее описано в статьях “Использование X-Accel-Redirect с Nginx для реализации контролир…” и “Использование Nginx Как Reverse-Proxy Сервера На [...]
April 10th, 2008 at 9:02 am
Про использование в lighttpd контролируемых скачек с помощью скриптов можно поподробнее?
April 24th, 2008 at 11:58 am
Спасибо за статью.
С заголовками таки что-то не то - FF и IE отрабатывают нормально, а вот Опера упорно предлагает сохранить в html …
June 16th, 2008 at 7:35 pm
Чтобы правильно отдавать тип и имя файла:
<?php
header(”Content-type: video/mpeg”);
header(”Content-Disposition: attachment; filename=\”file.mpeg\”");
//или подставить свой
Чтобы не задалбывали потоками reget, dmaster etc..
в nginx.conf
http {
limit_zone one $binary_remote_addr 10m;
…
location /files {
root /home/www/…/files;
internal;
limit_conn one 1;
}
где one-имя зоны, 1-количество подкл. с одного ip
June 16th, 2008 at 7:40 pm
Кстати, есть такая функция в php
string mime_content_type ( string filename)
не тестировал, но если работает - удобно