# 第四節:文件上傳
# 文件上傳:
文件上傳是網站開發中非常常見的功能。這里詳細講述如何在`Django`中實現文件的上傳功能。
## 前端HTML代碼實現:
1. 在前端中,我們需要填入一個`form`標簽,然后在這個`form`標簽中指定`enctype="multipart/form-data"`,不然就不能上傳文件。
2. 在`form`標簽中添加一個`input`標簽,然后指定`input`標簽的`name`,以及`type="file"`。
以上兩步的示例代碼如下:
```
<pre class="calibre12">```
<span class="hljs-tag"><<span class="hljs-class">form</span> <span class="hljs-class">action</span>=<span class="hljs-string">""</span> <span class="hljs-class">method</span>=<span class="hljs-string">"post"</span> <span class="hljs-class">enctype</span>=<span class="hljs-string">"multipart/form-data"</span>></span>
<span class="hljs-tag"><<span class="hljs-class">input</span> <span class="hljs-class">type</span>=<span class="hljs-string">"file"</span> <span class="hljs-class">name</span>=<span class="hljs-string">"myfile"</span>></span>
<span class="hljs-tag"></<span class="hljs-class">form</span>></span>
```
```
## 后端的代碼實現:
后端的主要工作是接收文件。然后存儲文件。接收文件的方式跟接收`POST`的方式是一樣的,只不過是通過`FILES`來實現。示例代碼如下:
```
<pre class="calibre12">```
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">save_file</span><span class="hljs-params">(file)</span>:</span>
<span class="hljs-keyword">with</span> open(<span class="hljs-string">'somefile.txt'</span>,<span class="hljs-string">'wb'</span>) <span class="hljs-keyword">as</span> fp:
<span class="hljs-keyword">for</span> chunk <span class="hljs-keyword">in</span> file.chunks():
fp.write(chunk)
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">index</span><span class="hljs-params">(request)</span>:</span>
<span class="hljs-keyword">if</span> request.method == <span class="hljs-string">'GET'</span>:
form = MyForm()
<span class="hljs-keyword">return</span> render(request,<span class="hljs-string">'index.html'</span>,{<span class="hljs-string">'form'</span>:form})
<span class="hljs-keyword">else</span>:
myfile = request.FILES.get(<span class="hljs-string">'myfile'</span>)
save_file(myfile)
<span class="hljs-keyword">return</span> HttpResponse(<span class="hljs-string">'success'</span>)
```
```
以上代碼通過`request.FILES`接收到文件后,再寫入到指定的地方。這樣就可以完成一個文件的上傳功能了。
## 使用模型來處理上傳的文件:
在定義模型的時候,我們可以給存儲文件的字段指定為`FileField`,這個`Field`可以傳遞一個`upload_to`參數,用來指定上傳上來的文件保存到哪里。比如我們讓他保存到項目的`files`文件夾下,那么示例代碼如下:
```
<pre class="calibre12">```
<span class="hljs-title"># models.py</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Article</span><span class="hljs-params">(models.Model)</span>:</span>
title = models.CharField(max_length=<span class="hljs-params">100</span>)
content = models.TextField()
thumbnail = models.FileField(upload_to=<span class="hljs-string">"files"</span>)
<span class="hljs-title"># views.py</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">index</span><span class="hljs-params">(request)</span>:</span>
<span class="hljs-keyword">if</span> request.method == <span class="hljs-string">'GET'</span>:
<span class="hljs-keyword">return</span> render(request,<span class="hljs-string">'index.html'</span>)
<span class="hljs-keyword">else</span>:
title = request.POST.get(<span class="hljs-string">'title'</span>)
content = request.POST.get(<span class="hljs-string">'content'</span>)
thumbnail = request.FILES.get(<span class="hljs-string">'thumbnail'</span>)
article = Article(title=title, content=content, thumbnail=thumbnail)
article.save()
<span class="hljs-keyword">return</span> HttpResponse(<span class="hljs-string">'success'</span>)
```
```
調用完`article.save()`方法,就會把文件保存到`files`下面,并且會將這個文件的路徑存儲到數據庫中。
## 指定`MEDIA_ROOT`和`MEDIA_URL`:
以上我們是使用了`upload_to`來指定上傳的文件的目錄。我們也可以指定`MEDIA_ROOT`,就不需要在`FielField`中指定`upload_to`,他會自動的將文件上傳到`MEDIA_ROOT`的目錄下。
```
<pre class="calibre12">```
MEDIA_ROOT = os.path.join(BASE_DIR,<span class="hljs-string">'media'</span>)
MEDIA_URL = <span class="hljs-string">'/media/'</span>
```
```
然后我們可以在`urls.py`中添加`MEDIA_ROOT`目錄下的訪問路徑。示例代碼如下:
```
<pre class="calibre12">```
<span class="hljs-keyword">from</span> django.urls <span class="hljs-keyword">import</span> path
<span class="hljs-keyword">from</span> front <span class="hljs-keyword">import</span> views
<span class="hljs-keyword">from</span> django.conf.urls.static <span class="hljs-keyword">import</span> static
<span class="hljs-keyword">from</span> django.conf <span class="hljs-keyword">import</span> settings
urlpatterns = [
path(<span class="hljs-string">''</span>, views.index),
] + static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)
```
```
如果我們同時指定`MEDIA_ROOT`和`upload_to`,那么會將文件上傳到`MEDIA_ROOT`下的`upload_to`文件夾中。示例代碼如下:
```
<pre class="calibre12">```
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Article</span><span class="hljs-params">(models.Model)</span>:</span>
title = models.CharField(max_length=<span class="hljs-params">100</span>)
content = models.TextField()
thumbnail = models.FileField(upload_to=<span class="hljs-string">"%Y/%m/%d/"</span>)
```
```
## 限制上傳的文件拓展名:
如果想要限制上傳的文件的拓展名,那么我們就需要用到表單來進行限制。我們可以使用普通的`Form`表單,也可以使用`ModelForm`,直接從模型中讀取字段。示例代碼如下:
```
<pre class="calibre12">```
<span class="hljs-title"># models.py</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Article</span><span class="hljs-params">(models.Model)</span>:</span>
title = models.CharField(max_length=<span class="hljs-params">100</span>)
content = models.TextField()
thumbnial = models.FileField(upload_to=<span class="hljs-string">'%Y/%m/%d/'</span>,validators=[validators.FileExtensionValidator([<span class="hljs-string">'txt'</span>,<span class="hljs-string">'pdf'</span>])])
<span class="hljs-title"># forms.py</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ArticleForm</span><span class="hljs-params">(forms.ModelForm)</span>:</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Meta</span>:</span>
model = Article
fields = <span class="hljs-string">"__all__"</span>
```
```
## 上傳圖片:
上傳圖片跟上傳普通文件是一樣的。只不過是上傳圖片的時候`Django`會判斷上傳的文件是否是圖片的格式(除了判斷后綴名,還會判斷是否是可用的圖片)。如果不是,那么就會驗證失敗。我們首先先來定義一個包含`ImageField`的模型。示例代碼如下:
```
<pre class="calibre12">```
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Article</span><span class="hljs-params">(models.Model)</span>:</span>
title = models.CharField(max_length=<span class="hljs-params">100</span>)
content = models.TextField()
thumbnail = models.ImageField(upload_to=<span class="hljs-string">"%Y/%m/%d/"</span>)
```
```
因為要驗證是否是合格的圖片,因此我們還需要用一個表單來進行驗證。表單我們直接就使用`ModelForm`就可以了。示例代碼如下:
```
<pre class="calibre12">```
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyForm</span><span class="hljs-params">(forms.ModelForm)</span>:</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Meta</span>:</span>
model = Article
fields = <span class="hljs-string">"__all__"</span>
```
```
**注意:使用ImageField,必須要先安裝Pillow庫:pip install pillow**
- Introduction
- 第一章:學前準備
- 第一節:虛擬環境
- 第二節:準備工作
- 第三節:Django介紹
- 第四節:URL組成部分
- 第二章:URL與視圖
- 第一節:第一個Django項目
- 第二節:視圖與URL分發器
- 第三章:模板
- 第一節:模板介紹
- 第二節:模板變量
- 第三節:常用標簽
- 第四節:常用過濾器
- 第五節:自定義過濾器
- 第七節:模版結構優化
- 第八節:加載靜態文件
- 第四章:數據庫
- 第一節:MySQL相關軟件
- 第二節:數據庫操作
- 第三節:ORM模型
- 第四節:模型常用字段
- 第五節:外鍵和表關系
- 第六節:增刪改查操作
- 第七節:查詢操作
- 第八節:QuerySet API
- 第九節:ORM模型遷移
- 第十節:ORM作業
- 第十一節:ORM作業參考答案
- 第十二節:Pycharm連接數據庫
- 第五章:視圖高級
- 第一節:限制請求method
- 第二節:頁面重定向
- 第三節:HttpRequest對象
- 第四節:HttpResponse對象
- 第五節:生成CSV文件
- 第六節:類視圖
- 第七節:錯誤處理
- 第六章:表單
- 第一節:表單概述
- 第二節:用表單驗證數據
- 第三節:ModelForm
- 第四節:文件上傳
- 第七章:cookie和session
- 第八章:上下文處理器和中間件
- 第一節:上下文處理器
- 第二節:中間件
- 第九章:安全
- 第一節:CSRF攻擊
- 第二節:XSS攻擊
- 第三節:點擊劫持攻擊
- 第四節:SQL注入
- 第十章:信號
- 第一節:什么是信號
- 第十一章:驗證和授權
- 第一節:概述
- 第二節:用戶對象
- 第三節:權限和分組
- 第十二章:Admin系統
- 第十三章:Django的緩存
- 第十四章:memcached
- 第十五章:Redis