Tải tệp lớn lên Django Rest Framework
Tôi cố gắng tải lên một tệp lớn (4GB) với PUT trên bộ xem DRF.
Trong quá trình tải lên, bộ nhớ của tôi ổn định. Ở mức 100%, quá trình máy chủ chạy python ngày càng chiếm nhiều RAM và bị giết bởi hạt nhân. Tôi có một dòng ghi nhật ký trong putphương thức này APIViewnhưng quá trình này đã bị ngắt trước khi gọi phương thức này.
Tôi sử dụng cài đặt này để buộc sử dụng tệp FILE_UPLOAD_HANDLERS = ["django.core.files.uploadhandler.TemporaryFileUploadHandler"]
Đỉnh cao trí nhớ này đến từ đâu? Tôi đoán nó cố gắng tải nội dung tệp trong bộ nhớ nhưng tại sao (và ở đâu)?
Thêm thông tin:
- Tôi đã thử DEBUG true và false
- Người chạy nằm trong một docker phía sau traefik nhưng không có giới hạn trong traefik AFAIK và tải lên đạt 100%
- Tôi chưa biết liệu tôi có nhận được hành vi tương tự với
daphnethay vì chạy máy chủ không - CHỈNH SỬA: sử dụng phía trước một
Content-Type multipart/form-data - CHỈNH SỬA: Tôi đã thử
FileUploadParservà(FormParser, MultiPartParser)cho parser_classes trongAPIView
Trả lời
TL; DR:
Không phải là vấn đề DRF hay Django, đó là vấn đề Daphne đã biết 2,5 năm . Giải pháp là sử dụng uvicorn, hypercorn hoặc một thứ gì đó khác trong lúc này.
Giải thích
Những gì bạn thấy ở đây không phải đến từ Django Rest Framework như:
- FileUploadParser được dùng để xử lý các tệp tải lên lớn, vì nó đọc tệp theo từng đoạn ;
- Chế độ xem của bạn không được thực thi sẽ loại trừ các trình phân tích cú pháp không được thực thi cho đến khi bạn truy cập thuộcrequest.FILES tính
Thực tế là bạn đang đề cập đến Daphne làm tôi nhớ đến câu trả lời SO này đề cập đến một vấn đề tương tự và chỉ ra một đoạn mã mà Daphne không xử lý tải lên tệp lớn vì nó tải toàn bộ phần thân trong RAM trước khi chuyển nó đến chế độ xem. (Mã vẫn còn trong nhánh chính của họ tại thời điểm viết bài)
Bạn đang thấy hành vi tương tự runservervì khi được cài đặt, Daphne sẽ thay thế lệnh chạy ban đầu bằng chính nó để cung cấp hỗ trợ WebSockets cho các mục đích của nhà phát triển.
Để đảm bảo rằng đó là thủ phạm thực sự, hãy thử tắt Kênh / chạy trình chạy Django mặc định và tự mình xem liệu ứng dụng của bạn có bị giết bởi OOM Killer hay không.
Tôi không biết liệu nó có hoạt động với phần còn lại django hay không, nhưng bạn có thể thử tách tệp de tệp.
[...]
anexo_files = request.FILES.getlist('anexo_file_'+str(k))
index = 0
for file in anexo_files:
index = index + 1
extension = os.path.splitext(str(file))[1]
nome_arquivo_anexo = 'media/uploads/' + os.path.splitext(str(file))[0] + "_" + str(index) + datetime.datetime.now().strftime("%m%d%Y%H%M%S") + extension
handle_uploaded_file(file, nome_arquivo_anexo)
AnexoProjeto.objects.create(
projeto=projeto,
arquivo_anexo = nome_arquivo_anexo
)
[...]
Handle_uploaded_file ở đâu
def handle_uploaded_file(f, nome_arquivo):
with open(nome_arquivo, 'wb+') as destination:
for chunk in f.chunks():
destination.write(chunk)