Ошибка памяти Django Filewrapper при обслуживании больших файлов

У меня есть такой код:

@login_required
def download_file(request):
    content_type = "application/octet-stream"
    download_name = os.path.join(DATA_ROOT, "video.avi")

    with open(download_name, "rb") as f:
        wrapper = FileWrapper(f, 8192)
        response = HttpResponse(wrapper, content_type=content_type)
    response['Content-Disposition'] = 'attachment; filename=blabla.avi'
    response['Content-Length'] = os.path.getsize(download_name)
    # response['Content-Length'] = _file.size
    return response

Кажется, это работает. Однако, если я загружаю больший файл (например, ~600 МБ), мое потребление памяти увеличивается на эти 600 МБ. После нескольких таких загрузок мой сервер выдает:

Внутренняя ошибка сервера: /download/ Traceback (последний вызов был последним):
Файл "/home/matous/.local/lib/python3.5/site-packages/django/core/handlers/exception.py", строка 35, во внутреннем ответе = файл get_response(запрос) "/home/matous/.local/lib/python3.5/site-packages/django/core/handlers/base.py", строка 128, в _get_response response = self.process_exception_by_middleware(e, запрос) Файл" /home/matous/.local/lib/ python3.5 / site-packages / django / core / handlers / base.py ", строка 126, в _get_response response = wrapped_callback(запрос, *callback_args, **callback_kwargs) файл"/home/matous/.local/lib/python3.5/site-packages/django/contrib/auth/decorators.py", строка 21, в _wrapped_view возвращать view_func(запрос, *args, **kwargs) файл"/media/matous/89104d3d-fa52-4b14-9c5d-9ec54ceebebb/home/matous/phd/emoapp/emoapp/mainapp/views.py", строка 118, в файле download_file response = HttpResponse(wrapper, content_type=content_type) Файл"/home/matous/.local/lib/python3.5/site-packages/django/http/response.py", строка 285, в init self.content = файл содержимого" /home/matous/.local/lib/ python3.5 / site-packages / django / http / response.py ", строка 308, в содержимом content = b''.join(self.make_bytes(chunk) для фрагмента в значении) MemoryError

Что я делаю не так? Можно ли как-то настроить его для потоковой передачи по частям с жесткого диска без этой безумной памяти?

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

1 ответ

Решение

Попробуй использовать StreamingHttpResponse вместо этого это поможет, это именно то, что вы ищете.

Можно ли как-то настроить его для потоковой передачи по частям с жесткого диска без этой безумной памяти?

import os
from django.http import StreamingHttpResponse
from django.core.servers.basehttp import FileWrapper #django <=1.8
from wsgiref.util import FileWrapper #django >1.8

@login_required
def download_file(request):
   file_path = os.path.join(DATA_ROOT, "video.avi")
   filename = os.path.basename(the_file)
   chunk_size = 8192
   response = StreamingHttpResponse(
       FileWrapper(open(file_path, 'rb'), chunk_size),
       content_type="application/octet-stream"
   )
   response['Content-Length'] = os.path.getsize(the_file)    
   response['Content-Disposition'] = "attachment; filename=%s" % filename
   return response

Это приведет к потоковой передаче вашего файла без загрузки в память; в качестве альтернативы, вы можете использовать FileResponse,

который является подклассом StreamingHttpResponse оптимизирован для бинарных файлов.

Другие вопросы по тегам