Django FileResponse падает при работе с большими файлами
...У меня есть приложение Django с большим количеством данных и базой данных MariaDB, работающее на Raspberry Pi (OS = Debian 12). Приложение использует Daphne в качестве Webserver, потому что в нем также есть компоненты Django Channels (Websocket). Теперь я хочу реализовать функцию резервного копирования, которая автоматически сбрасывает базу данных, архивирует ее вместе с другими файлами данных и позволяет браузеру автоматически загружать ZIP-файл. Поэтому я сделал представление для загрузки:
def downbackup(request):
if request.user.is_superuser:
filename = '../temp/backup/backup.zip'
sourcefile = open(filename, 'rb')
return FileResponse(sourcefile)
else:
return(HttpResponse('No Access.'))
Вид вызывается из соответствующего шаблона по url и все в порядке. Пока мы не столкнулись с большими файлами в реальной жизни. В этом случае (размер файлов около 6 ГБ) Daphne немедленно прекращает работу, а Raspi падает так глубоко, что мне приходится включать и выключать питание. Кроме того, в Monitorix я вижу огромные скачки потребления памяти после этих сбоев. Но ни в журналах Daphne, ни в журналах Django (включая отладочную настройку), ни в journalctl нет никаких сообщений об ошибках. Nginx (обратный прокси) сообщает о таймауте восходящего канала (504). Кажется, я узнал из документации Django, что FileResponse буферизирует файл, чтобы избежать потребления памяти, но что-то здесь должно быть не так. Есть идеи, рекомендации?
Для эффективной работы с большими файлами вы можете использовать StreamingHttpResponse
вместо FileResponse
в Django.
from django.http import StreamingHttpResponse
import os
def download_large_file(request):
# Path to the large file
file_path = '/path/to/your/large/file'
def file_iterator(file_path, chunk_size=8192):
with open(file_path, 'rb') as f:
while True:
chunk = f.read(chunk_size)
if not chunk:
break
yield chunk
response = StreamingHttpResponse(file_iterator(file_path))
response['Content-Length'] = os.path.getsize(file_path)
return response
I would strongly advise not to use Django as a file server, it is not intended to use that way. Typically nginx is used for example with an X-Accel-Redirect
[nginx-doc] such that Django essentially says where the file is, and nginx, can then return the file in a manner that only nginx provides the file when an X-Accel-Redirect
is present.
Итак, по сути, вы работаете с:
def downbackup(request):
if request.user.is_superuser:
response = HttpResponse()
response['Content-Disposition'] = 'attachment; filename=backup.zip'
response['X-Accel-Redirect'] = '/protected/temp/backup/backup.zip'
return response
else:
return HttpResponse('No Access.')
и, таким образом, сервер nginx обслуживает защищенный путь с:
location /protected/ {
internal;
alias /path/one/above/tmp;
}
Это не только обеспечит отсутствие проблем с буферизацией и парообразованием nginx, но и может привести к кэшированию nginx определенных файлов, что повысит эффективность работы веб-сервера.
Ключевое слово internal
в конфиге /protected/
означает, что вы не можете делать запросы извне к этому пути. То есть человек не может зайти на /protected/temp/backup/backup.zip
, он может использоваться только для внутренней связи в nginx с "подсерверами".