В прошлой реализации хранилища файлов с хэшированием имен файлы были доступны клиентам сайта по хэшированному имени, что не всегда удобно — файлы, передаваемые клиентам сайта, не имели осмысленных имён. Решая эту проблему, все хранилище было переработано и теперь представляет собой приложение. Кроме того, требуется дополнительно настроить frontend-сервер. Для начала несколько слов о самом принципе работы системы:

- 1. Клиент запрашивает файл у веб-сервера (frontend, в нашем случае это nginx).
- 2. nginx перенаправляет запрос к Django (backend), согласно параметрам локации (location), которой соответствует URI запрошенного файла.
- 3. Django принимает запрос, обрабатывает URL и возвращает nginx'у ответ с кодом 200, содержащий реальный путь до файла (в заранее заданной локации) в заголовке X-Accel-Redirect, а также тип содержимого файла в заголовке Content-type, основанный на расширении изначального имени файла.
- 4, 5, 6. nginx обрабатывает путь в X-Accel-Redirect и в ответ на исходный запрос (пункт 1) клиента передаёт ему файл из директории, соответствующей локации, если файл найден. Заголовок Content-type, полученный от Django, при этом сохраняется.
Теперь обо всем по порядку. В первую очередь настроим frontend:
server {
listen mysite.ru:80;
server_name mysite.ru www.mysite.ru;
# Нам совершенно не обязательно проксировать все запросы.
# Поэтому ограничиваемся проверкой запросов вида
# http://mysite.ru/media/upload/some_file.ext
# Передаём изменённый GET-запрос Django. В ответ мы должны получить ответ с
# заголовком X-Accel-Redirect.
location /media/upload {
rewrite ^/media/upload/(.*) /nginx/?filename=upload/$1 break;
fastcgi_pass unix:/path/to/django's/fcgi.sock;
include /etc/nginx/fastcgi_params;
}
# Эта локация отражает реальный путь до директории хранилища наших файлов.
# Именно сюда Django будет ссылаться в X-Accel-Redirect.
# Прямой доступ к файлам клиентам сайта запрещён (для этого нужна директива internal).
location /sha1storage {
internal;
alias /path/to/project/media/sha1storage;
}
# Запрещаем клиентам сайта прямые запросы к Django в локацию /nginx
location /nginx {
internal;
}
# Запрещаем клиентам сайта прямой доступ к файлам хранилища в обход Django
location /media/sha1storage {
internal;
}
# Локация с файлами, доступными для загрузки клиентам сайта не из sha1storage.
# Сюда Django перенаправит запрос посредством X-Accel-Redirect, если не
# найдёт файл в sha1storage.
location /upload {
internal;
alias /home/www/www51/betalabs.ru/media/upload;
}
}
Как это все в итоге работает: nginx получает запрос от клиента. Если путь запрашиваемого файла начинается с /media/upload/, то делается запрос к Django по адресу вида '/nginx/?filename=upload/filename.ext', с передачей в GET-переменной filename пути до файла, с указанием изначального имени. Обратите внимание, что в запросе к Django обрезается /media/, поскольку Django всегда работает с файлами внутри директории, указанной в MEDIA_ROOT.
Для настройки django нам понадобиться приложение
SHA1Storage.
ВНИМАНИЕ. На данный момент приложение находится в стадии beta-тестирования и имеет некоторые ограничения в настройке, которые, я надеюсь, в ближайшее время будут ликвидированы. Если вы найдете какие-то баги, буду очень благодарен за багрепорт.
Для установки добавляем его в INSTALLED_APPS:
INSTALLED_APPS = (
'sha1storage',
)
# Если хотите использовать это хранилище как основное, можно указать параметр DEFAULT_FILE_STORAGE.
DEFAULT_FILE_STORAGE = 'sha1storage.storage.SHA1FilenameFileSystemStorage'
Если вы хотите использовать хранилище только для отдельных файлов, то к полю файла в модели необходимо добавить свойство storage.
В urls.py необходимо добавить обработку URL обработчика sha1storage:
urlpatterns = patterns('',
(r'nginx/', 'sha1storage.views.get_real_filename'),
);
В принципе это все. Теперь при загрузке файла его имя будет хэшироваться, и он будет сохраняться в папку /path/to/project/media/sha1storage/upload_to_mode_field_path/. При этом модели будет передаваться не хэш от имени файла, а само имя. Из дополнительных особенностей: все файлы, кроме стандартной графики, sha1storage возвращает с заголовком Content-Disposition: attachment, в качестве полумеры для закрытия XSS-уязвимостей, пока не реализованы более строгие алгоритмы защиты.