您好,登錄后才能下訂單哦!
怎么在django中上傳文件?很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。
文件或圖片一般通過表單進行。用戶在前端點擊文件上傳,然后以POST方式將數據和文件提交到服務器。服務器在接收到POST請求后需要將其存儲在服務器上的某個地方。Django默認的存儲地址是相對于根目錄的/media/文件夾,存儲的默認文件名就是文件本來的名字。上傳的文件如果不大于2.5MB,會先存入服務器內存中,然后再寫入磁盤。如果上傳的文件很大,Django會把文件先存入臨時文件,再寫入磁盤。
Django默認處理方式會出現一個問題,所有文件都存儲在一個文件夾里。不同用戶上傳的有相同名字的文件可能會相互覆蓋。另外用戶還可能上傳一些不安全的文件如js和exe文件,我們必需對允許上傳文件的類型進行限制。因此我們在利用Django處理文件上傳時必需考慮如下3個因素:
設置存儲上傳文件的文件夾地址
對上傳文件進行重命名
對可接受的文件類型進行限制(表單驗證)
注意:以上事項對于上傳圖片是同樣適用的。
Django文件上傳一般有3種方式(如下所示)。我們會針對3種方式分別提供代碼示范。
使用一般的自定義表單上傳,在視圖中手動編寫代碼處理上傳的文件
使用由模型創建的表單(ModelForm)上傳,使用form.save()方法自動存儲
使用Ajax實現文件異步上傳,上傳頁面無需刷新即可顯示新上傳的文件
Ajax文件上傳部分見Django與Ajax交互篇。
我們先使用django-admin startproject命令創建一個叫file_project的項目,然后cd進入file_project, 使用python manage.py startapp創建一個叫file_upload的app。
我們首先需要將file_upload這個app加入到我們項目里,然后設置/media/和/STATIC_URL/文件夾。我們上傳的文件都會放在/media/文件夾里。我們還需要使用css和js這些靜態文件,所以需要設置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)
使用Django上傳文件創建模型不是必需,然而如果我們需要對上傳文件進行系統化管理,模型還是很重要的。我們的File模型包括file和upload_method兩個字段。我們通過upload_to選項指定了文件上傳后存儲的地址,并對上傳的文件名進行了重命名。
#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,你還需要手動編寫代碼存儲上傳文件。
本項目一共包括3個urls, 分別對應普通表單上傳,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'), ]
我們先定義一個一般表單FileUploadForm,并通過clean方法對用戶上傳的文件進行驗證,如果上傳的文件名不以jpg, pdf或xlsx結尾,將顯示表單驗證錯誤信息。關于表單的自定義和驗證更多內容見Django基礎: 表單forms的設計與使用。
#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方法對表單字段進行驗證時,別忘了return驗證過的數據,即cleaned_data。只有返回了cleaned_data, 視圖中才可以使用form.cleaned_data.get(‘xxx')獲取驗證過的數據。
對應一般文件上傳的視圖file_upload方法如下所示。當用戶的請求方法為POST時,我們通過form.cleaned_data.get('file')獲取通過驗證的文件,并調用自定義的handle_uploaded_file方法來對文件進行重命名,寫入文件。如果用戶的請求方法不為POST,則渲染一個空的FileUploadForm在upload_form.html里。我們還定義了一個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。存在數據中字段的是相對地址,而不是絕對地址。
構建文件寫入絕對路徑時請用os.path.join方法,因為不同系統文件夾分隔符不一樣。寫入文件前一個良好的習慣是使用os.path.exists檢查目標文件夾是否存在,如果不存在先創建文件夾,再寫入。
上傳表單模板upload_form.html代碼如下:
#file_upload/templates/upload_form.html {% extends "file_upload/base.html" %} {% block content %} {% if heading %} <h4>{{ heading }}</h4> {% 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 %} <h4>File List</h4> <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 %}
注意:
對于上傳的文件我們可以調用file.url, file.name和file.size來查看上傳文件的鏈接,地址和大小。
上傳文件的大小默認是以B顯示的,數字非常大。使用Django模板過濾器filesizeformat可以將文件大小顯示為人們可讀的方式,如MB,KB。
使用ModelForm上傳是小編我推薦的上傳方式,前提是你已經在模型中通過upload_to選項自定義了用戶上傳文件存儲地址,并對文件進行了重命名。我們首先要自定義自己的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方法非常簡單,只需調用form.save()即可,無需再手動編寫代碼寫入文件。
#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'} )
看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。