Обслуживание больших файлов (с большими нагрузками) в Django - PullRequest
29 голосов
/ 22 декабря 2011

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

    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  

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

Ответы [ 5 ]

55 голосов
/ 22 декабря 2011

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

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

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
14 голосов
/ 22 декабря 2011

Вы можете использовать метод 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's 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 голосов
/ 23 августа 2016

Лучше использовать 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
3 голосов
/ 04 апреля 2012

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.

2 голосов
/ 22 декабря 2011

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

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

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

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

...