Реализация возможности скачивания директорий пользователями сайта

в 9:25, , рубрики: cgi, fcgiwrap, nginx, Веб-разработка, метки: , ,

Есть небольшой закрытый сайт, на котором выкладывается музыка альбомами и пользователи сайта имеют возможность эти альбомы слушать прямиком из браузера. На сервере альбомы хранятся в виде директорий, внутри которых хранятся сами музыкальные композиции, которые по требованию плеера отдаются nginx-ом.
Все было хорошо, пока пользователи не захотели скачивать понравившиеся альбомы целиком на свои компьютеры.

Под катом раскажу как мы реализовали это.

Первое решение, которое приходит в голову — создавать архив каждого альбома и отдавать его пользователю.
Главная причина по которой мы отказались от этого варианта: у нас много музыки и создание архива для каждого альбома означает, что количество дискового пространства нужно умножить на два — пока мы себе этого позволить не можем.

Нам показалось, что идеально было бы формировать архив альбома по запросу и на лету. Если при создании архива отключить сжатие, то накладные расходы должны быть небольшими, ну и сами расходы для нас не особо критичны — на данный момент у нас большой запас по процессорному времени и оперативной памяти, в отличие от постоянного хранилища.

Поиск модуля для nginx, или каких-то других готовых решений, результата не дал — будем реализовывать сами — задача не сложная.

Для архивации будем использовать стандартную программу zip, без сжатия и данные не будем писать в файл, а направим сразу в stdout. Для такой цели логичнее было бы использовать tar, но у нас обычные пользователи и для многих будет загадкой что делать с tar или tar.gz архивом.

Теперь нам надо как-то передать сжатые данные nginx-у, который отдаст их пользователю.
Для этих целей на шелле был написан простенький cgi-скрипт:

#!/bin/bash

# Корневая директория, от нее будут идти пути в архиве и внутри нее хранятся все альбомы
homeDir="/storage/media/audio"

# Имя скачиваемой директории передается в строке запроса, строку запроса надо декодировать
downloadDir=$(echo $QUERY_STRING | sed -f urldecode.sed)

# Переходим в корневую директорию
pushd "$homeDir" > /dev/null

# Если директория существует - на лету упаковываем ее в zip без сжатия и возвращаем в качестве тела ответа
if [ -d $downloadDir ]  && [ ! -z $downloadDir ]
then
    echo "Content-Type: application/octet-stream"
    echo "Content-Disposition: attachment; filename=$downloadDir.zip"
    echo ""
    /usr/bin/zip -r -0 - "$downloadDir"
else
    echo "Status: 404 Not Found"
    echo "Content-Type: text/html"
    echo ""
    echo "<h1>404 File not found!</h1>"
fi

# Возвращаемся в начальную директорию
popd > /dev/null

Скрипт прост, думаю дополнительно его комментировать будет излишне.

Так как имя директории приходит url-кодированное, мы его раскодируем, для этого используем sed и небольшой скрипт для него, скрипт берем здесь и кладём рядом с нашим cgi-скриптом.

Все знают, что nginx поддерживает FastCGI и не поддерживает CGI, чтобы наш скрипт все таки работал будем использовать Fcgiwrap.

Ставим:

apt-get install fcgiwrap

И конфигурируем:

# support for cgi scripts (require fcgiwrap)
location ~ .cgi$
{
    gzip off;
    try_files $uri =404;
 
    # pass scripts to fcgiwrap
    fastcgi_pass unix:/var/run/fcgiwrap.socket;
    
    # Используем стандартные параметры
    include /etc/nginx/fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_ignore_client_abort off;
}

Перезапускаем nginx и можно пользоваться: /download.cgi? имя_директории_для скачивания

Автор: KOS_MOS

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js