Pull to refresh

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

Reading time 3 min
Views 7.6K
Есть небольшой закрытый сайт, на котором выкладывается музыка альбомами и пользователи сайта имеют возможность эти альбомы слушать прямиком из браузера. На сервере альбомы хранятся в виде директорий, внутри которых хранятся сами музыкальные композиции, которые по требованию плеера отдаются 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 ] && [[ "$downloadDir" != *\/* ]] && [[ "$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? имя_директории_для скачивания
Tags:
Hubs:
+15
Comments 15
Comments Comments 15

Articles