Previous Entry Поделиться Next Entry
Nginx, post_action. Как раздавать файлы по уникальному URL и отмечать окончание скачивания.
abarmotik

Условия


Задача возникла при работе над файл-шарой.
Для противодействия недобросовестным пользователям нужно организовать:

  1. уникальную ссылку на файл (что бы ей мог воспользоваться только получивший ее юзер)

  2. учет полного скачивания файла (т.е. отмечать скачивание по его завершению, а не по началу)



Файлы раздает nginx, а шифрует-дешифрует ссылки и учитывает раздачу бэк-енд сервер.

Схема работы:



  • сервер генерит ссылку, шифруя в ней ИД файла и IP юзера

  • по ссылке запрос пользователя попадает в лапы nginx'у

  • nginx перенаправляет запрос на бэк-енд сервер

  • бэк-енд расшифровывает ссылку и возвращает нгинксу путь к файлу

  • нгинкс раздает файл и по завершению отправляет отчет на бэк-енд.



Подробнее:


Создание ссылки пропускаю — тут все просто.

Что бы отдать файл, нгинкс должен знать его настоящий путь. Для этого передаем запрос бэк-енду с помощью директивы proxy_pass:

    proxy_pass http://127.0.0.1:8888/nginxds/linkDecoder?domain=$host&path=$request_uri;
    proxy_set_header  X-Real-IP  $remote_addr;



Бэк-енд получив шифрованную ссылку и ИП юзера, получает из нее ИД файла, узнает путь к нему и возвращает в виде хттп-хедера X-Accel-Redirect:

    String link = httpRequest.getParameter("path");
    File file = decryptLink(link);
    httpResponse.addHeader("X-Accel-Redirect", file.getPath());



Получив такой хедер, нгинкс переходит к соотв. location'у, который и отдает файл.

В этом же локейшыне нужно прописать директиву post_action @postDownload. Она выполнится по окончанию отдачи файла. В локейшыне @postDownload передаем запрос бэк-енду (с помощью proxy_pass).

Внимание, особенность!!! Если proxy_pass содержит переменные запроса ($uri, $host и т.п.) и отдача файла занимает некоторое время, то proxy_pass не срабатывает! Видимо запрос в нгинксе столько не живет. Т.е. обращения к бэк-енду не происходит. Лечится это «запоминанием» нужных данных запроса в переменные (в предыдущем локейшыне).

Не правильно. При передаче больших файлов proxy_pass не сработает.

    # В этот локейшн переходим по хедеру X-Accel-Redirect от бэк-енда.
    # (предполагается, что все раздаваемые файлы лежат в папке /storage)
    location /storage {
        post_action @postDownload;
        root /;
        internal;
    }
    
    # с такими параметрами proxy_pass на больших файлах не сработает
    location @postDownload {
        proxy_pass http://127.0.0.1:8888/nginxds/postDownload?domain=$host&uri=$uri;
        proxy_set_header  X-Real-IP  $remote_addr;
        proxy_set_header  BytesSent  $body_bytes_sent;
    }



Правильно. Сохраним нужные данные запроса в переменные.

    # В этот локейшн переходим по хедеру X-Accel-Redirect от бэк-енда.
    # (предполагается, что все раздаваемые файлы лежат в папке /storage)
    location /storage {
        set $postURI $uri;
        set $postIP $remote_addr;
        set $postHOST $host;
        
        post_action @postDownload;

        root /;
        internal;
    }

    location @postDownload {
        proxy_pass http://127.0.0.1:8888/nginxds/postDownload?domain=$postHOST&uri=$postURI;
        proxy_set_header  X-Real-IP  $postIP;
        proxy_set_header  BytesSent  $body_bytes_sent;
    }



Пост-запрос выполняется даже в случае досрочного завершения скачивания. По этому нужно передать бэк-енду сколько байт было реально отдано (proxy_set_header BytesSent $body_bytes_sent;)
На бэк-енде, учитывая хедеры BytesSent, range и реальный размер файла можно определить окончание скачивания.

Конфиг нгинкса



limit_zone lim  $binary_remote_addr 10m;

server {
    listen 80;
    server_name www.example.com;
    

    # Здесь обрабатывается запрос на скачивание файла.
    # урл имеет вид http://example.com/download/-encrypted-file-id-
    location /download {
        set $limit_rate 10k;
        limit_conn lim  1;

	# передаем запрос бэк-енду на расшифровку. В ответе ожидаем хедер X-Accel-Redirect 
        proxy_pass http://127.0.0.1:8888/nginxds/linkDecoder?domain=$host&path=$request_uri;
        proxy_set_header  X-Real-IP  $remote_addr;
        
        add_header Content-Length $content_length;
        add_header Content-Disposition attachment;
    }

    # В этот локейшн переходим по хедеру X-Accel-Redirect от бэк-енда (см. предыдущий локейшн)
    # Предполагается, что все раздаваемые файлы лежат в папке /storage
    location /storage {
        set $postURI $uri;
        set $postIP $remote_addr;
        set $postHOST $host;
        
        post_action @postDownload;

        root /;
        internal;
    }

    # по завершению скачивания файла передаем на бэк-енд переменные установленные
    # в предыдущем локейшыне и отданный объем (в байтах)
    location @postDownload {
        proxy_pass http://127.0.0.1:8888/nginxds/postDownload?domain=$postHOST&uri=$postURI;
        proxy_set_header  X-Real-IP  $postIP;
        proxy_set_header  BytesSent  $body_bytes_sent;
    }
}

Метки: ,

  • 1
Вот этот склероз очень на баг похож

  • 1
?

Log in