目錄
- Django文件上傳需要考慮的重要事項(xiàng)
- Django文件上傳的3種常見方式
- 項(xiàng)目創(chuàng)建與設(shè)置
- 創(chuàng)建模型
- URLConf配置
- 使用一般表單上傳文件
- 使用ModelForm上傳文件
- GitHub源碼地址
- 小結(jié)
Django文件上傳需要考慮的重要事項(xiàng)
文件或圖片一般通過表單進(jìn)行。用戶在前端點(diǎn)擊文件上傳,然后以POST方式將數(shù)據(jù)和文件提交到服務(wù)器。服務(wù)器在接收到POST請求后需要將其存儲在服務(wù)器上的某個(gè)地方。Django默認(rèn)的存儲地址是相對于根目錄的/media/文件夾,存儲的默認(rèn)文件名就是文件本來的名字。上傳的文件如果不大于2.5MB,會先存入服務(wù)器內(nèi)存中,然后再寫入磁盤。如果上傳的文件很大,Django會把文件先存入臨時(shí)文件,再寫入磁盤。
Django默認(rèn)處理方式會出現(xiàn)一個(gè)問題,所有文件都存儲在一個(gè)文件夾里。不同用戶上傳的有相同名字的文件可能會相互覆蓋。另外用戶還可能上傳一些不安全的文件如js和exe文件,我們必需對允許上傳文件的類型進(jìn)行限制。因此我們在利用Django處理文件上傳時(shí)必需考慮如下3個(gè)因素:
- 設(shè)置存儲上傳文件的文件夾地址
- 對上傳文件進(jìn)行重命名
- 對可接受的文件類型進(jìn)行限制(表單驗(yàn)證)
注意:以上事項(xiàng)對于上傳圖片是同樣適用的。
Django文件上傳的3種常見方式
Django文件上傳一般有3種方式(如下所示)。我們會針對3種方式分別提供代碼示范。
- 使用一般的自定義表單上傳,在視圖中手動(dòng)編寫代碼處理上傳的文件
- 使用由模型創(chuàng)建的表單(ModelForm)上傳,使用form.save()方法自動(dòng)存儲
- 使用Ajax實(shí)現(xiàn)文件異步上傳,上傳頁面無需刷新即可顯示新上傳的文件
Ajax文件上傳部分見Django與Ajax交互篇。
項(xiàng)目創(chuàng)建與設(shè)置
我們先使用django-admin startproject命令創(chuàng)建一個(gè)叫file_project的項(xiàng)目,然后cd進(jìn)入file_project, 使用python manage.py startapp創(chuàng)建一個(gè)叫file_upload的app。
我們首先需要將file_upload這個(gè)app加入到我們項(xiàng)目里,然后設(shè)置/media/和/STATIC_URL/文件夾。我們上傳的文件都會放在/media/文件夾里。我們還需要使用css和js這些靜態(tài)文件,所以需要設(shè)置STATIC_URL。
#file_project/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'file_upload',# 新增
]
STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, "static"), ]
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
#file_project/urls.py
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path('file/', include("file_upload.urls")),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
創(chuàng)建模型
使用Django上傳文件創(chuàng)建模型不是必需,然而如果我們需要對上傳文件進(jìn)行系統(tǒng)化管理,模型還是很重要的。我們的File模型包括file和upload_method兩個(gè)字段。我們通過upload_to選項(xiàng)指定了文件上傳后存儲的地址,并對上傳的文件名進(jìn)行了重命名。
#file_upload/models.py
from django.db import models
import os
import uuid
# Create your models here.
# Define user directory path
def user_directory_path(instance, filename):
ext = filename.split('.')[-1]
filename = '{}.{}'.format(uuid.uuid4().hex[:10], ext)
return os.path.join("files", filename)
class File(models.Model):
file = models.FileField(upload_to=user_directory_path, null=True)
upload_method = models.CharField(max_length=20, verbose_name="Upload Method")
注意:如果你不使用ModelForm,你還需要手動(dòng)編寫代碼存儲上傳文件。
URLConf配置
本項(xiàng)目一共包括3個(gè)urls, 分別對應(yīng)普通表單上傳,ModelForm上傳和顯示文件清單。
#file_upload/urls.py
from django.urls import re_path, path
from . import views
# namespace
app_name = "file_upload"
urlpatterns = [
# Upload File Without Using Model Form
re_path(r'^upload1/$', views.file_upload, name='file_upload'),
# Upload Files Using Model Form
re_path(r'^upload2/$', views.model_form_upload, name='model_form_upload'),
# View File List
path('file/', views.file_list, name='file_list'),
]
使用一般表單上傳文件
我們先定義一個(gè)一般表單FileUploadForm,并通過clean方法對用戶上傳的文件進(jìn)行驗(yàn)證,如果上傳的文件名不以jpg, pdf或xlsx結(jié)尾,將顯示表單驗(yàn)證錯(cuò)誤信息。關(guān)于表單的自定義和驗(yàn)證更多內(nèi)容見Django基礎(chǔ): 表單forms的設(shè)計(jì)與使用。
#file_upload/forms.py
from django import forms
from .models import File
# Regular form
class FileUploadForm(forms.Form):
file = forms.FileField(widget=forms.ClearableFileInput(attrs={'class': 'form-control'}))
upload_method = forms.CharField(label="Upload Method", max_length=20,
widget=forms.TextInput(attrs={'class': 'form-control'}))
def clean_file(self):
file = self.cleaned_data['file']
ext = file.name.split('.')[-1].lower()
if ext not in ["jpg", "pdf", "xlsx"]:
raise forms.ValidationError("Only jpg, pdf and xlsx files are allowed.")
# return cleaned data is very important.
return file
注意: 使用clean方法對表單字段進(jìn)行驗(yàn)證時(shí),別忘了return驗(yàn)證過的數(shù)據(jù),即cleaned_data。只有返回了cleaned_data, 視圖中才可以使用form.cleaned_data.get(‘xxx')獲取驗(yàn)證過的數(shù)據(jù)。
對應(yīng)一般文件上傳的視圖file_upload方法如下所示。當(dāng)用戶的請求方法為POST時(shí),我們通過form.cleaned_data.get('file')獲取通過驗(yàn)證的文件,并調(diào)用自定義的handle_uploaded_file方法來對文件進(jìn)行重命名,寫入文件。如果用戶的請求方法不為POST,則渲染一個(gè)空的FileUploadForm在upload_form.html里。我們還定義了一個(gè)file_list方法來顯示文件清單。
#file_upload/views.py
from django.shortcuts import render, redirect
from .models import File
from .forms import FileUploadForm, FileUploadModelForm
import os
import uuid
from django.http import JsonResponse
from django.template.defaultfilters import filesizeformat
# Create your views here.
# Show file list
def file_list(request):
files = File.objects.all().order_by("-id")
return render(request, 'file_upload/file_list.html', {'files': files})
# Regular file upload without using ModelForm
def file_upload(request):
if request.method == "POST":
form = FileUploadForm(request.POST, request.FILES)
if form.is_valid():
# get cleaned data
upload_method = form.cleaned_data.get("upload_method")
raw_file = form.cleaned_data.get("file")
new_file = File()
new_file.file = handle_uploaded_file(raw_file)
new_file.upload_method = upload_method
new_file.save()
return redirect("/file/")
else:
form = FileUploadForm()
return render(request, 'file_upload/upload_form.html',
{'form': form, 'heading': 'Upload files with Regular Form'}
)
def handle_uploaded_file(file):
ext = file.name.split('.')[-1]
file_name = '{}.{}'.format(uuid.uuid4().hex[:10], ext)
# file path relative to 'media' folder
file_path = os.path.join('files', file_name)
absolute_file_path = os.path.join('media', 'files', file_name)
directory = os.path.dirname(absolute_file_path)
if not os.path.exists(directory):
os.makedirs(directory)
with open(absolute_file_path, 'wb+') as destination:
for chunk in file.chunks():
destination.write(chunk)
return file_path
注意:
- handle_uploaded_file方法里文件寫入地址必需是包含/media/的絕對路徑,如果/media/files/xxxx.jpg,而該方法返回的地址是相對于/media/文件夾的地址,如/files/xxx.jpg。存在數(shù)據(jù)中字段的是相對地址,而不是絕對地址。
- 構(gòu)建文件寫入絕對路徑時(shí)請用os.path.join方法,因?yàn)椴煌到y(tǒng)文件夾分隔符不一樣。寫入文件前一個(gè)良好的習(xí)慣是使用os.path.exists檢查目標(biāo)文件夾是否存在,如果不存在先創(chuàng)建文件夾,再寫入。
上傳表單模板upload_form.html代碼如下:
#file_upload/templates/upload_form.html
{% extends "file_upload/base.html" %}
{% block content %}
{% if heading %}
h3>{{ heading }}/h3>
{% endif %}
form action="" method="post" enctype="multipart/form-data" >
{% csrf_token %}
{{ form.as_p }}
button class="btn btn-info form-control " type="submit" value="submit">Upload/button>
/form>
{% endblock %}
顯示文件清單模板file_list.html代碼如下所示:
# file_upload/templates/file_list.html
{% extends "file_upload/base.html" %}
{% block content %}
h3>File List/h3>
p> a href="/file/upload1/" rel="external nofollow" >RegularFormUpload/a> | a href="/file/upload2/" rel="external nofollow" >ModelFormUpload/a>
| a href="/file/upload3/" rel="external nofollow" >AjaxUpload/a>/p>
{% if files %}
table class="table table-striped">
tbody>
tr>
td>Filename URL/td>
td>Filesize/td>
td>Upload Method/td>
/tr>
{% for file in files %}
tr>
td>a href="{{ file.file.url }}" rel="external nofollow" >{{ file.file.url }}/a>/td>
td>{{ file.file.size | filesizeformat }}/td>
td>{{ file.upload_method }}/td>
/tr>
{% endfor %}
/tbody>
/table>
{% else %}
p>No files uploaded yet. Please click a href="{% url 'file_upload:file_upload' %}" rel="external nofollow" >here/a>
to upload files./p>
{% endif %}
{% endblock %}
注意:
- 對于上傳的文件我們可以調(diào)用file.url, file.name和file.size來查看上傳文件的鏈接,地址和大小。
- 上傳文件的大小默認(rèn)是以B顯示的,數(shù)字非常大。使用Django模板過濾器filesizeformat可以將文件大小顯示為人們可讀的方式,如MB,KB。
使用ModelForm上傳文件
使用ModelForm上傳是小編我推薦的上傳方式,前提是你已經(jīng)在模型中通過upload_to選項(xiàng)自定義了用戶上傳文件存儲地址,并對文件進(jìn)行了重命名。我們首先要自定義自己的FileUploadModelForm,由File模型重建的。代碼如下所示:
#file_upload/forms.py
from django import forms
from .models import File
# Model form
class FileUploadModelForm(forms.ModelForm):
class Meta:
model = File
fields = ('file', 'upload_method',)
widgets = {
'upload_method': forms.TextInput(attrs={'class': 'form-control'}),
'file': forms.ClearableFileInput(attrs={'class': 'form-control'}),
}
def clean_file(self):
file = self.cleaned_data['file']
ext = file.name.split('.')[-1].lower()
if ext not in ["jpg", "pdf", "xlsx"]:
raise forms.ValidationError("Only jpg, pdf and xlsx files are allowed.")
# return cleaned data is very important.
return file
使用ModelForm處理文件上傳的視圖model_form_upload方法非常簡單,只需調(diào)用form.save()即可,無需再手動(dòng)編寫代碼寫入文件。
#file_upload/views.py
from django.shortcuts import render, redirect
from .models import File
from .forms import FileUploadForm, FileUploadModelForm
import os
import uuid
from django.http import JsonResponse
from django.template.defaultfilters import filesizeformat
# Create your views here.
# Upload File with ModelForm
def model_form_upload(request):
if request.method == "POST":
form = FileUploadModelForm(request.POST, request.FILES)
if form.is_valid():
form.save() # 一句話足以
return redirect("/file/")
else:
form = FileUploadModelForm()
return render(request, 'file_upload/upload_form.html',
{'form': form,'heading': 'Upload files with ModelForm'}
)
模板跟前面一樣,這里就不展示了。
GitHub源碼地址
https://github.com/shiyunbo/django-file-upload-download
小結(jié)
本文提供并解讀了利用Django上傳文件的3種主要方式(一般表單上傳,ModelForm上傳和Ajax上傳)及示范代碼。
以上就是django上傳文件的三種方式的詳細(xì)內(nèi)容,更多關(guān)于django上傳文件的資料請關(guān)注腳本之家其它相關(guān)文章!
您可能感興趣的文章:- Django 如何實(shí)現(xiàn)文件上傳下載
- Django和Ueditor自定義存儲上傳文件的文件名
- 詳解Django自定義圖片和文件上傳路徑(upload_to)的2種方式
- 基于django和dropzone.js實(shí)現(xiàn)上傳文件
- python中Django文件上傳方法詳解
- Django后端分離 使用element-ui文件上傳方式
- Django Admin 上傳文件到七牛云的示例代碼
- Django實(shí)現(xiàn)任意文件上傳(最簡單的方法)
- Django 解決上傳文件時(shí),request.FILES為空的問題
- Django中文件上傳和文件訪問微項(xiàng)目的方法
- django 文件上傳功能的相關(guān)實(shí)例代碼(簡單易懂)