...

вторник, 24 сентября 2013 г.

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

Есть небольшой закрытый сайт, на котором выкладывается музыка альбомами и пользователи сайта имеют возможность эти альбомы слушать прямиком из браузера. На сервере альбомы хранятся в виде директорий, внутри которых хранятся сами музыкальные композиции, которые по требованию плеера отдаются 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? имя_директории_для скачивания


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:



Комментариев нет:

Отправить комментарий