Filuppladdning¶
När Django hanterar en filuppladdning placeras filinformationen i request.FILES
(för mer information om objektet request
se dokumentationen för request and response objects). I det här dokumentet förklaras hur filer lagras på disk och i minne och hur du anpassar standardbeteendet.
Varning
Det finns säkerhetsrisker om du accepterar uppladdat innehåll från icke betrodda användare! Se säkerhetsguidens ämne om Innehåll som laddats upp av användare för mer information om begränsning.
Grundläggande filuppladdningar¶
Tänk dig ett formulär som innehåller en FileField
:
forms.py
¶from django import forms
class UploadFileForm(forms.Form):
title = forms.CharField(max_length=50)
file = forms.FileField()
En vy som hanterar detta formulär kommer att få filinformationen i request.FILES
, som är en ordbok som innehåller en nyckel för varje FileField
(eller ImageField
, eller annan FileField
subklass) i formuläret. Så data från ovanstående formulär skulle vara tillgängliga som request.FILES['file']
.
Observera att request.FILES <django.http.HttpRequest.FILES>`
endast kommer att innehålla data om förfrågningsmetoden var POST
, minst ett filfält faktiskt postades och <form>
som postade förfrågan har attributet enctype="multipart/form-data"
. Annars kommer request.FILES
att vara tomt.
För det mesta kommer du att skicka filinformationen från request
till formuläret enligt beskrivningen i Binder uppladdade filer till ett formulär. Detta skulle se ut ungefär som:
Vyer.py
¶from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import UploadFileForm
# Imaginary function to handle an uploaded file.
from somewhere import handle_uploaded_file
def upload_file(request):
if request.method == "POST":
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
handle_uploaded_file(request.FILES["file"])
return HttpResponseRedirect("/success/url/")
else:
form = UploadFileForm()
return render(request, "upload.html", {"form": form})
Observera att vi måste skicka request.FILES
till formulärets konstruktör; det är så här fildata binds till ett formulär.
Här är ett vanligt sätt att hantera en uppladdad fil:
def handle_uploaded_file(f):
with open("some/file/name.txt", "wb+") as destination:
for chunk in f.chunks():
destination.write(chunk)
Genom att loopa över UploadedFile.chunks()
istället för att använda read()
säkerställs att stora filer inte överbelastar systemets minne.
Det finns några andra metoder och attribut som är tillgängliga för UploadedFile
-objekt; se UploadedFile
för en fullständig referens.
Hantera uppladdade filer med en modell¶
Om du sparar en fil på en Model
med en FileField
, kan du använda en ModelForm
som gör processen mycket enklare. Filobjektet kommer att sparas på den plats som anges av upload_to
-argumentet för motsvarande FileField
när du anropar form.save()
:
from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import ModelFormWithFileField
def upload_file(request):
if request.method == "POST":
form = ModelFormWithFileField(request.POST, request.FILES)
if form.is_valid():
# file is saved
form.save()
return HttpResponseRedirect("/success/url/")
else:
form = ModelFormWithFileField()
return render(request, "upload.html", {"form": form})
Om du konstruerar ett objekt manuellt kan du tilldela filobjektet från request.FILES
till filfältet i modellen:
from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import UploadFileForm
from .models import ModelWithFileField
def upload_file(request):
if request.method == "POST":
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
instance = ModelWithFileField(file_field=request.FILES["file"])
instance.save()
return HttpResponseRedirect("/success/url/")
else:
form = UploadFileForm()
return render(request, "upload.html", {"form": form})
Om du konstruerar ett objekt manuellt utanför en begäran kan du tilldela ett File
liknande objekt till FileField
:
from django.core.management.base import BaseCommand
from django.core.files.base import ContentFile
class MyCommand(BaseCommand):
def handle(self, *args, **options):
content_file = ContentFile(b"Hello world!", name="hello-world.txt")
instance = ModelWithFileField(file_field=content_file)
instance.save()
Ladda upp flera filer¶
Om du vill ladda upp flera filer med ett formulärfält skapar du en underklass av fältets widget och ställer in klassattributet allow_multiple_selected
på True
.
För att alla sådana filer ska kunna valideras av ditt formulär (och fältets värde ska inkludera dem alla) måste du också underklassa FileField
. Se nedan för ett exempel.
Fält för flera filer
Django kommer sannolikt att ha ett ordentligt stöd för flera filfält någon gång i framtiden.
forms.py
¶from django import forms
class MultipleFileInput(forms.ClearableFileInput):
allow_multiple_selected = True
class MultipleFileField(forms.FileField):
def __init__(self, *args, **kwargs):
kwargs.setdefault("widget", MultipleFileInput())
super().__init__(*args, **kwargs)
def clean(self, data, initial=None):
single_file_clean = super().clean
if isinstance(data, (list, tuple)):
result = [single_file_clean(d, initial) for d in data]
else:
result = [single_file_clean(data, initial)]
return result
class FileFieldForm(forms.Form):
file_field = MultipleFileField()
Åsidosätt sedan metoden form_valid()
i din FormView
-underklass för att hantera flera filuppladdningar:
Vyer.py
¶from django.views.generic.edit import FormView
from .forms import FileFieldForm
class FileFieldFormView(FormView):
form_class = FileFieldForm
template_name = "upload.html" # Replace with your template.
success_url = "..." # Replace with your URL or reverse().
def form_valid(self, form):
files = form.cleaned_data["file_field"]
for f in files:
... # Do something with each file.
return super().form_valid(form)
Varning
Detta gör att du kan hantera flera filer endast på formulärnivå. Tänk på att du inte kan använda den för att lägga till flera filer på en enda modellinstans (i ett enda fält), till exempel även om den anpassade widgeten används med ett formulärfält som är relaterat till en modell FileField
.
Uppladdningshanterare¶
När en användare laddar upp en fil skickar Django filinformationen till en uppladdningshanterare - en liten klass som hanterar filinformationen när den laddas upp. Uppladdningshanterare definieras initialt i inställningen FILE_UPLOAD_HANDLERS
, som har standardvärdet:
[
"django.core.files.uploadhandler.MemoryFileUploadHandler",
"django.core.files.uploadhandler.TemporaryFileUploadHandler",
]
Tillsammans tillhandahåller MemoryFileUploadHandler
och TemporaryFileUploadHandler
Djangos standardbeteende för filuppladdning genom att läsa små filer i minnet och stora filer på disken.
Du kan skriva egna handlers som anpassar hur Django hanterar filer. Du kan till exempel använda anpassade hanterare för att genomdriva kvoter på användarnivå, komprimera data i farten, rendera förloppsbalkar och till och med skicka data till en annan lagringsplats direkt utan att lagra dem lokalt. Se Skriva anpassade uppladdningshanterare för detaljer om hur du kan anpassa eller helt ersätta uppladdningsbeteendet.
Var uppladdade data lagras¶
Innan du sparar uppladdade filer måste data lagras någonstans.
Som standard, om en uppladdad fil är mindre än 2,5 megabyte, kommer Django att hålla hela innehållet i uppladdningen i minnet. Det innebär att när filen sparas sker endast en läsning från minnet och en skrivning till disken och det går därför mycket snabbt.
Men om en uppladdad fil är för stor kommer Django att skriva den uppladdade filen till en temporär fil som lagras i ditt systems temporära katalog. På en Unix-liknande plattform betyder det att du kan förvänta dig att Django genererar en fil som heter något i stil med /tmp/tmpzfp6I6.upload
. Om en uppladdning är tillräckligt stor kan du se den här filen växa i storlek när Django strömmar data till disken.
Dessa specifikationer – 2,5 megabyte; /tmp
; etc. – är ”rimliga standardvärden” som kan anpassas enligt beskrivningen i nästa avsnitt.
Ändra beteende för uppladdningshanterare¶
Det finns några inställningar som styr Djangos filuppladdningsbeteende. Se Filuppladdningsinställningar för detaljer.
Ändra uppladdningshanterare i farten¶
Ibland kräver särskilda vyer olika uppladdningsbeteende. I dessa fall kan du åsidosätta uppladdningshanterare per begäran genom att ändra request.upload_handlers
. Som standard innehåller den här listan de uppladdningshanterare som anges av FILE_UPLOAD_HANDLERS
, men du kan ändra listan som du skulle göra med vilken annan lista som helst.
Anta till exempel att du har skrivit en ProgressBarUploadHandler
som ger feedback om uppladdningsförloppet till någon form av AJAX-widget. Du skulle lägga till den här hanteraren till dina uppladdningshanterare så här:
request.upload_handlers.insert(0, ProgressBarUploadHandler(request))
Du skulle förmodligen vilja använda list.insert()
i det här fallet (istället för append()
) eftersom en progress bar-hanterare skulle behöva köras före alla andra hanterare. Kom ihåg att uppladdningshanterarna behandlas i ordning.
Om du vill ersätta uppladdningshanterarna helt och hållet kan du tilldela en ny lista:
request.upload_handlers = [ProgressBarUploadHandler(request)]
Observera
Du kan bara ändra uppladdningshanterare före åtkomst till request.POST
eller request.FILES
- det är inte meningsfullt att ändra uppladdningshanterare efter att uppladdningshanteringen redan har börjat. Om du försöker ändra request.upload_handlers
efter att ha läst från request.POST
eller request.FILES
kommer Django att ge ett felmeddelande.
Därför bör du alltid modifiera uppladdningshanterare så tidigt som möjligt i din vy.
Dessutom nås request.POST
av CsrfViewMiddleware
som är aktiverat som standard. Detta innebär att du måste använda csrf_exempt()
på din vy för att du ska kunna ändra uppladdningshanterarna. Du måste sedan använda csrf_protect()
på den funktion som faktiskt behandlar begäran. Observera att detta innebär att hanterarna kan börja ta emot filuppladdningen innan CSRF-kontrollerna har gjorts. Exempel på kod:
from django.views.decorators.csrf import csrf_exempt, csrf_protect
@csrf_exempt
def upload_file_view(request):
request.upload_handlers.insert(0, ProgressBarUploadHandler(request))
return _upload_file_view(request)
@csrf_protect
def _upload_file_view(request):
# Process request
...
Om du använder en klassbaserad vy måste du använda csrf_exempt()
på dess dispatch()
metod och csrf_protect()
på den metod som faktiskt behandlar förfrågan. Exempel på kod:
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.csrf import csrf_exempt, csrf_protect
@method_decorator(csrf_exempt, name="dispatch")
class UploadFileView(View):
def setup(self, request, *args, **kwargs):
request.upload_handlers.insert(0, ProgressBarUploadHandler(request))
super().setup(request, *args, **kwargs)
@method_decorator(csrf_protect)
def post(self, request, *args, **kwargs):
# Process request
...