Подтвердить что ты не робот

Обслуживание больших файлов (с большими нагрузками) в Django

Я использую метод для загрузки загрузок, но поскольку он не был защищен, я решил изменить его. (метод был ссылкой на исходный файл в хранилище, но риск заключался в том, что все, у кого есть ссылка, могли загрузить файл!), поэтому я теперь обслуживаю файл через свои представления, таким образом, только пользователи с разрешением могут загрузить файл, но я замечаю высокую нагрузку на сервер, в то время как есть много одновременных запросов на загрузку файлов. здесь часть моего кода, который обрабатывает загрузки для пользователей (см. файл - это изображение)

    image = Image.open ("the path to file")
    response = HttpResponse(mimetype = 'image/png' )
    response['Content-Disposition'] = 'attachment: filename=%s.png' % filename
    image.save(response , "png")
    return response  

Есть ли лучшие способы для обслуживания файлов, сохраняя безопасность и понижая нагрузку на сервер? спасибо заранее:)

4b9b3361

Ответ 1

Ваше открытие изображения загружает его в память, и именно это приводит к увеличению нагрузки при интенсивном использовании. Как сообщил Мартин, реальное решение состоит в том, чтобы напрямую обслуживать файл.

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

import os
import mimetypes
from django.http import StreamingHttpResponse
from django.core.servers.basehttp import FileWrapper


def download_file(request):
   the_file = '/some/file/name.png'
   filename = os.path.basename(the_file)
   chunk_size = 8192
   response = StreamingHttpResponse(FileWrapper(open(the_file, 'rb'), chunk_size),
                           content_type=mimetypes.guess_type(the_file)[0])
   response['Content-Length'] = os.path.getsize(the_file)    
   response['Content-Disposition'] = "attachment; filename=%s" % filename
   return response

Ответ 2

Вы можете использовать метод sendfile, как описано в этом .

Практически вам это нужно (c & p):

response = HttpResponse(mimetype='application/force-download')
response['Content-Disposition'] = 'attachment; filename=%s' % smart_str(file_name)
response['X-Sendfile'] = smart_str(path_to_file)
# It usually a good idea to set the 'Content-Length' header too.
# You can also set any other required headers: Cache-Control, etc.
return response

Для этого требуется mod_xsendfile (который также поддерживается nginx или lighty)

Ответ 3

FileWrapper не будет работать, если установлен GZipMiddleware (Django 1.4 и ниже): https://code.djangoproject.com/ticket/6027

При использовании GZipMiddleware практическое решение заключается в написании подкласса FileWrapper следующим образом:

from wsgiref.util import FileWrapper
class FixedFileWrapper(FileWrapper):
    def __iter__(self):
        self.filelike.seek(0)
        return self

import mimetypes, os
my_file = '/some/path/xy.ext'
response = HttpResponse(FixedFileWrapper(open(my_file, 'rb')), content_type=mimetypes.guess_type(my_file)[0])
response['Content-Length'] = os.path.getsize(my_file)
response['Content-Disposition'] = "attachment; filename=%s" % os.path.basename(my_file)
return response

Как и в случае с Python 2.5, нет необходимости импортировать FileWrapper из Django.

Ответ 4

Если вы собираетесь подавать очень небольшое количество таких запросов, любое решение, требующее обслуживания вашего контента через django, не будет масштабируемым. Для того, чтобы что-то масштабировать в будущем, вы, вероятно, захотите переместить хранилище содержимого и обслуживать его на отдельный сервер, а затем это не сработает.

Рекомендуемым способом было бы сохранить статический контент, обслуживаемый более легким сервером (например, nginx). Чтобы добавить безопасность, передайте статическому серверу токен из django, установив файл cookie или через параметры get.

Маркер должен иметь следующие значения: timestamp, filename, userid. Он должен быть подписан через некоторый ключ с помощью приложения django.

Затем напишите небольшой модуль nginx, который проверяет токен и что пользователь действительно имеет доступ к файлу. Он также должен проверить, что токен недостаточно стар, проверяя метку времени.

Ответ 5

Лучше использовать FileRespose, является подклассом StreamingHttpResponse, оптимизированным для двоичных файлов. Он использует wsgi.file_wrapper, если он предоставлен сервером wsgi, иначе он выгружает файл небольшими фрагментами.

import os
from django.http import FileResponse
from django.core.servers.basehttp import FileWrapper


def download_file(request):
    _file = '/folder/my_file.zip'
    filename = os.path.basename(_file)
    response = FileResponse(FileWrapper(file(filename, 'rb')), content_type='application/x-zip-compressed')
    response['Content-Disposition'] = "attachment; filename=%s" % _file
    return response