当Django处理一个文件上传时,文件数据最终放在 request.FILES
(更多关于 request
对象参见文档 request and response objects )本文档解释文件如何存储在磁盘和内存中,以及如何自定义默认行为。
警告
如果您接受来自不受信任用户的上载内容,则存在安全风险!参见安全指南主题 用户上载的内容 有关缓解措施的详细信息。
考虑一个包含 FileField
:
from django import forms
class UploadFileForm(forms.Form):
title = forms.CharField(max_length=50)
file = forms.FileField()
处理此表单的视图将在 request.FILES
,这是一个字典,其中包含每个 FileField
(或) ImageField
,或其他 FileField
子类)。因此,上述表单中的数据可以作为 request.FILES['file']
.
注意 request.FILES
仅当请求方法为 POST
,至少有一个文件字段实际已发布,并且 <form>
发布请求的具有属性 enctype="multipart/form-data"
. 否则, request.FILES
将为空。
大多数情况下,您将从 request
按照中所述的形式 将上载的文件绑定到表单 . 这看起来像:
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})
注意我们必须通过 request.FILES
到表单的构造函数中;这是文件数据绑定到表单中的方式。
以下是处理上载文件的常见方法:
def handle_uploaded_file(f):
with open("some/file/name.txt", "wb+") as destination:
for chunk in f.chunks():
destination.write(chunk)
循环过度 UploadedFile.chunks()
而不是使用 read()
确保大文件不会占用系统内存。
有一些其他方法和属性可用于 UploadedFile
对象;参见 UploadedFile
作为完整的参考。
如果要在 Model
用一个 FileField
,使用 ModelForm
使这个过程更容易。文件对象将保存到 upload_to
对应的参数 FileField
调用时 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})
如果要手动构造对象,则可以从 request.FILES
到模型中的文件字段::
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})
如果在请求之外手动构造对象,则可以将 File
的LIKE对象 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()
如果要使用一个表单域上传多个文件,请创建该域的微件的子类并设置 allow_multiple_selected
属性设置为 True
。
为了让您的表单验证所有此类文件(并且使该字段的值包含所有这些文件),您还必须子类化 FileField
。请参见下面的示例。
多文件字段
Django很可能在未来的某个时候拥有适当的多文件现场支持。
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()
然后重写 post
你的方法 FormView
处理多个文件上载的子类:
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 post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
files = form.cleaned_data["file_field"]
for f in files:
... # Do something with each file.
return super().form_valid()
警告
这将允许您仅在表单级别处理多个文件。请注意,您不能使用它在单个模型实例上放置多个文件(例如,在单个字段中),即使自定义小部件与与模型相关的表单域一起使用 FileField
。
当用户上载文件时,django将文件数据传递给 上传处理程序 --一个小类,在文件数据上传时处理它。上载处理程序最初在 FILE_UPLOAD_HANDLERS
设置,默认为:
[
"django.core.files.uploadhandler.MemoryFileUploadHandler",
"django.core.files.uploadhandler.TemporaryFileUploadHandler",
]
一起 MemoryFileUploadHandler
和 TemporaryFileUploadHandler
提供Django的默认文件上载行为,即将小文件读取到内存中,将大文件读取到磁盘上。
您可以编写自定义处理程序来定制django处理文件的方式。例如,您可以使用自定义处理程序强制执行用户级配额、动态压缩数据、呈现进度条,甚至直接将数据发送到另一个存储位置,而不在本地存储数据。见 正在写入自定义上载处理程序 有关如何自定义或完全替换上载行为的详细信息。
在保存上载的文件之前,数据需要存储在某个地方。
默认情况下,如果上载的文件小于2.5兆字节,Django将在内存中保存上载的全部内容。这意味着保存文件只涉及从内存中读取和写入磁盘,因此速度非常快。
但是,如果上载的文件太大,Django会将上载的文件写入存储在系统临时目录中的临时文件。在类似Unix的平台上,这意味着您可以期望Django生成一个名为 /tmp/tmpzfp6I6.upload
. 如果上载足够大,您可以看到当Django将数据流到磁盘上时该文件的大小增加。
这些细节——2.5兆字节; /tmp
;等等——是“合理的默认值”,可以按照下一节的描述进行定制。
有一些设置控制Django的文件上传行为。见 File Upload Settings 有关详细信息。
有时特定视图需要不同的上载行为。在这些情况下,可以通过修改 request.upload_handlers
. 默认情况下,此列表将包含由 FILE_UPLOAD_HANDLERS
,但可以像修改任何其他列表一样修改该列表。
例如,假设你写了 ProgressBarUploadHandler
这提供了关于上传到某种Ajax小部件的进度的反馈。您可以将此处理程序添加到上载处理程序中,如下所示:
request.upload_handlers.insert(0, ProgressBarUploadHandler(request))
你可能想用 list.insert()
在这种情况下(而不是 append()
)因为进度条处理程序需要运行 之前 任何其他处理程序。请记住,上载处理程序是按顺序处理的。
如果要完全替换上载处理程序,可以指定一个新列表:
request.upload_handlers = [ProgressBarUploadHandler(request)]
备注
只能修改上载处理程序 之前 访问 request.POST
或 request.FILES
--在上载处理已经开始后更改上载处理程序是没有意义的。如果你试图修改 request.upload_handlers
在阅读之后 request.POST
或 request.FILES
Django将抛出一个错误。
因此,您应该尽可能早地在视图中修改上载处理程序。
也, request.POST
被访问 CsrfViewMiddleware
默认启用。这意味着你需要使用 csrf_exempt()
允许您更改上载处理程序。然后你需要使用 csrf_protect()
在实际处理请求的函数上。注意,这意味着处理程序可以在CSRF检查完成之前开始接收文件上传。示例代码:
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
如果您使用的是基于类的视图,则需要使用 csrf_exempt()
在ITS上 dispatch()
方法和方法 csrf_protect()
关于实际处理请求的方法。示例代码::
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
12月 18, 2023