Pull to refresh

Делаем свой webfile

Reading time 4 min
Views 12K
Отчего-то всегда хотел сделать свой сервис для загрузки файлов. Всевозможные slil/zalil не устраивали своей скоростью. ifolder — обилием рекламы. Пользовался не очень популярным (от этого он ни чуть хуже не становился) сервисом up.spbland.ru. Но это как-то не правильно. И тут я решил написать свой сервис. Не буду вдаваться в подробности и рутину, только концепция.

Требования перед ресурсом:
— загрузка файлов методом POST не менее 100 мб, хотя почему бы и не 1000?
— обязательная визуализация процесса, т.е. upload progressbar
— возможность докачки файлов пользователями (во время скачивания)
— исключить возможность установки прямых ссылок на файлы на сторонних форумах, сайтах. Только через site.ru/ID

Сперва расскажу об upload progressbar:
Во время приема большого количества multipart/form-data информации apache складывает все в /tmp, а уже после окончания загрузки (т.е. при поступлении всего запроса на web сервер) передает управление к php, который в свою очередь делает move_uploaded_file. По своей природе php устроен так, что процесс А не может знать на какой стадии загрузки находится процесс Б. Именно для этого и придумали эксперементально расширение: uploadprogress. По мимо input type=«file», в форму необходимо добавить input type=«hidden» name=«UPLOAD_IDENTIFIER» value=«какое-то число», позже по этому идентификатору можно будет при помощи функции uploadprogress_get_info() отслеживать состояние загрузки: кол-во полученных байт, всего байт, скорость и другие параметры. В моем случае я через AJAX обращался с скрипту раз в секунду и получал актуальную информацию.

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

Плохо — это значит принимать файлы и отдавать файлы через php. Чуть-чуть улучшить этот путь можно было бы заручившись поддержкой HTTP 206 Partial Content. Однако этот путь — путь латания дыр, не заслуживающий внимания.

Круто: отдачей файлов должен заниматься Apache, а лучше nginx. Во первых эти программы в полной мере поддерживаю спецификацию HTTP/1.1, во вторых Apache всегда будет работать быстрее, чем интерпретируемый php. В nginx для отдачи файлов реализован специальный механизм X-Accel-Redirect. Суть заключается в том, что nginx устанавливается в качестве прозрачного прокси сервера. Nginx обрабатывает, поступающий от пользователя, запрос к файлу site.ru/somefiles/123.mp3 и передает его на Apache&php. Далее либо средствами nginx, либо через mod_rewrite

RewriteRule ^/somefiles/(.*)$ /get.php?path=$1 [L]

запрос сводится к /get.php?path=123.mp3
В php анализируя ip адрес клиента, сессии и т.п. принимается решение о выдаче файла, либо об отказе. Далее php передает специальным образом сформированный заголовок:

header("X-Accel-Redirect: /files/123.mp3")

nginx перехватывает управление на себя и уже сам начинает отдавать файл своими средствами, делая это в миллион раз эффективнее чем apache. Однако у такого метода есть свой косяк: при загрузке файла на сервер nginx полностью получает весь запрос, и только потом передает его в apache. До тех пор, пока nginx полностью не получит весь файл, apache даже не будет знать, что происходит загрузка, что делает невозможным работу upload progress bar.

Вывод: загрузка файлов должная производится напрямую через apache, а отдача через nginx. В моем случае можно было бы повесить nginx (либо apache) на какой-нибудь другой порт. Но заставлять пользователей, напичканных файрволами, качать с порта отлично от 80 — не комильфо. По этому правильнее было бы повесить nginx на отдельный адрес, например download-0.site.ru (при такой концепции позже можно было бы добавить еще серверов). Но поскольку я не имел возможности использовать 2 адреса, от планетарного варианта файлового хостинга пришлось отказаться.

Однако я по прежнему хотел сохранить все поставленные ранее задачи выполненными.

Нормально. И тут на помощь мне пришел: Rewritemap

Соорудив конструкцию подобно этой:

Rewritemap ipmap txt:/home/anton/iplist.txt
RewriteCond ${ipmap:%{REMOTE_ADDR}} =allow
RewriteRule ^([0-9]+)/download/ /xfiles/$1 [L]
RewriteRule ^([0-9]+)/download/ /$1/ [L,R=302]
RewriteRule ^([0-9]+)/? /view.php?id=$1 [L]


я получил возможность переадресовывать ссылки вида site.ru/100200300/download/22.mp3 на файл /xfiles/100200300 (где реально расположен файл) в случае наличия ip адреса клиента в файл iplist.txt, либо происходила переадресация на страницу site.ru/100200300 с captcha, которая в случае правильного указания кода на картинке занесет ip клиента в файл iplist.txt и переадресует его вновь через Location на
site.ru/100200300/download/22.mp3, но в этот раз произойдет уже реальная отдача файла.

Файл iplist.txt имеет следующий формат:

77.1.1.1 allow # 1207100906

После решетки у меня указан timestamp, который сообщий специальному сервисному скрипту когда следует удалить эту строчку. Сервисный скрипт запускает раз в час через crontab.

В результате я получил достаточно правильную и надежную систему. Пожалуй ее недостатком является неэффективное расходование озу, т.к. каждый процесс apache (с mpm модулем prefork) может одновременно обслуживать только одного клиента, при этом сам процесс потребляет примерно 10 мб памяти. Если предположить, что с сервера начнет одновременно качать 1000 модемщиков в 10 потоков каждый, произойдет коллапс и должно не хватить памяти. Но от этого меня спасет директива из Httpd.conf MaxClients. С nginx такой проблемы быть в принципе не могло. В добавок nginx обладает неплохим функционалом по ограничению пользователей, например, по кол-ву одновременных подключений с одного адреса.

Пути улучшения до планетарного хостинга:
— переход на nginx в качестве frontend
— создание распределенной системы с сервером приема фалов в центе
— отказаться от хранения всех файлов в одной папке /xfiles/, создать еще 100 подпапок внутри xfiles основываясь на первых 2х символах.
— перейти на SAS диски, поскольку в масштабах сети интернет это не обмен единичными тяжелыми файлами, а постоянная загрузка сотен мелких файлов (музыки, фотографий)

Рабочая версия описанного: up.giga.su

P.S. Мой первый хабрапост
Tags:
Hubs:
+125
Comments 142
Comments Comments 142

Articles