Django 2.1.3 視圖層 文件上傳

總目錄 | File 對象


當Django處理文件上傳時,文件數據最終放入 request.FILES(有關request對象的更多信息, 請參閱 請求和響應對象 的文檔)。本文檔說明了文件如何存儲在磁盤和內存中,以及如何自定義默認行爲。

警告

如果您接受來自不受信任的用戶的上傳內容,則存在安全風險!有關詳細信息,請參閱安全指南中有關 用戶上傳內容 的主題 。

1.上傳

1.1 自定義form上傳

譯者注:此部分爲我自己添加的,幫助新手實現一個上傳的流程,其他人可以選擇性跳過。
(1)定義HTML部分,文件是polls/upload.html

   <h3>使用自己的form</h3>
    <form action="{% url 'polls:upload_file2' %}" method="POST" enctype="multipart/form-data">
        {% csrf_token %}
         <input type="file" name="upfile">
        <input type="submit" value="提交2">
    </form>

(2)配置URLconf

path('uploads2/',views.upload_file2,name='upload_file2'),

(3)定義視圖處理文件上傳的函數

def handle_uploaded_file2(f):
    with open('temp.PNG', 'wb+') as destination:
        for chunk in f.chunks():
            destination.write(chunk)
    print("upload ok")

def upload_file2(request):
    if request.method == 'POST':
        if request.FILES:
            handle_uploaded_file2(request.FILES['upfile'])
            return HttpResponse("上傳成功!")

    return render(request, 'polls/upload.html')

(4)訪問uploads2/,並上傳文件,結果如下,在工程目錄內生成了新的圖片文件
在這裏插入圖片描述

1.2 基本文件上傳

考慮包含一個 FileField字段的簡單的表單:

#forms.py 
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']來訪問。

請注意,請求方法必須是POST並且<form>標籤必須包含屬性enctype="multipart/form-data"。否則, request.FILES 將是空的。

大多數情況下,您只需將文件數據從request傳遞到表單中,如上傳的文件綁定到表單中所述。看起來像是這樣的:

#views.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})

請注意,我們必須把 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

譯者注
上面就是一個上傳文件並處理的基本流程,只是少了界面顯示,這裏補充一下

    <form action="{% url 'polls:upload_file' %}" method="POST" enctype="multipart/form-data">
        {% csrf_token %}
         {{ form }}
        <input type="submit" value="提交">
    </form>

1.3 使用模型處理上傳的文件

如果您將文件保存在一個 Model上的 FileField字段,則ModelForm 可以更輕鬆地使用此過程。調用form.save()保存該文件對象時,將會被保存到由FileField字段中upload_to屬性指定的位置 :

# views.py
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})

譯者補充
定義一個Model如下

# models.py
class UsrInfo(models.Model):
    title = models.CharField(max_length=64)
    tipc = models.FileField(upload_to="static")

定義一個ModelForm子類表單

class ModelFormWithFileField(forms.ModelForm):
    class Meta:
        model = UsrInfo
        fields = '__all__'

定義HTML文件

    <form action="{% url 'polls:upload_file3' %}" method="POST" enctype="multipart/form-data">
        {% csrf_token %}
        {{ mform }}
        <input type="submit" value="提交3">
    </form>

配置URLconf

path('uploads3/',views.upload_file3,name='upload_file3'),

訪問uploads3/並上傳文件,最終的結果是這樣的
在這裏插入圖片描述


如果要手動構建對象,只需將文件對象分配給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})

1.4 上傳多個文件

如果要使用一個表單字段上傳多個文件,請設置字段widget的HTML屬性爲multiple

#forms.py 
from django import forms

class FileFieldForm(forms.Form):
    file_field = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))

然後處理多個文件上傳:

#views.py 
from django.views.generic.edit import FormView
from .forms import FileFieldForm

class FileFieldView(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)
        files = request.FILES.getlist('file_field')
        if form.is_valid():
            for f in files:
                ...  # Do something with each file.
            return self.form_valid(form)
        else:
            return self.form_invalid(form)

譯者注:這裏是多個文件,所以使用request.FILES.getlist()方法來獲取所有文件。

2. 上傳處理程序

當用戶上傳文件時,Django將文件數據傳遞給上傳處理程序 - 一個在上傳時處理文件數據的小類。上傳處理程序最初在FILE_UPLOAD_HANDLERS設置中定義,默認爲:

["django.core.files.uploadhandler.MemoryFileUploadHandler",
 "django.core.files.uploadhandler.TemporaryFileUploadHandler"]

MemoryFileUploadHandlerTemporaryFileUploadHandler一起提供Django默認的文件上傳行爲(讀小文件到內存中,大文件到磁盤)。

您可以編寫自定義處理程序來自定義Django處理文件的方式。例如,您可以使用自定義處理程序來強制執行用戶級配額,動態壓縮數據,渲染進度條,甚至直接將數據發送到另一個存儲位置,而無需在本地存儲。有關 如何自定義或完全替換上載行爲的詳細信息,請參閱編寫自定義上傳處理程序

2.1 上傳數據存儲在哪裏?

在保存上傳的文件之前,需要將數據存儲在某處。

默認情況下,如果上傳的文件小於2.5M,Django會將上傳的全部內容保存在內存中。這意味着保存文件只涉及從內存中讀取和寫入磁盤,因此非常快。

但是,如果上傳的文件太大,Django會將上傳的文件寫入存儲在系統臨時目錄中的臨時文件中。在類Unix的平臺上,這意味着你可以期待Django生成一個類似/tmp/tmpzfp6I6.upload的文件。如果文件足夠大,您可以觀看此文件的大小增長,因爲Django將數據流式傳輸到磁盤上。

這些細節 - 2.5M; /tmp; 等 - 只是“合理的默認值”,可以按照下一節中的描述進行自定義。

2.2 更改上傳處理程序行爲

有一些設置可以控制Django的文件上傳行爲。有關詳情,請參閱 文件上傳設置

2.3 動態修改上傳處理程序

有時特定視圖需要不同的上傳行爲。在這些情況下,您可以通過覆蓋上傳處理程序 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.POST或request.FILES後嘗試修改request.upload_handlers,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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章