# 第四节:文件上传
# 文件上传:
文件上传是网站开发中非常常见的功能。这里详细讲述如何在`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