文件上传是网站开发中非常常见的功能。接下来就将详细讲述如何在Django中实现文件的上传功能。
即上传文件我们想要它显示的名字
),以及type=“file”。新建一个file.html的文件,body中写入:
<form action="" method="post" enctype="multipart/form-data">
<input type="file" name="myfile">
<input type="submit" value="提交">
form>
后端的主要工作是接收文件。然后存储文件。接收文件的方式跟接收POST的方式是一样的,只不过是通过FILES来实现。示例代码如下:
在views中添加视图函数:
from django.shortcuts import render
from django.views.generic import View
from django.http import HttpResponse
# Create your views here.
class IndexView(View):
def get(self,request):
return render(request,'file.html')
def post(self,request):
myfile = request.FILES.get('myfile')
with open('files.txt','wb') as fp:
for chunk in myfile.chunks():
fp.write(chunk)
return HttpResponse('success')
然后在urls中添加映射,我们就可以运行项目了,
然后我们就可以选取本地的文件进行上传了。
在定义模型的时候,我们可以给存储文件的字段指定为FileField,这个Field可以传递一个upload_to参数,用来指定上传上来的文件保存到哪里。比如我们让他保存到项目的files文件夹下,那么示例代码如下:
在models中新建一个模型:
from django.db import models
# Create your models here.
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
file = models.FileField(upload_to='files')
因为我们指定了上传到files
文件夹中,所以我们需要手动在项目的根目录下新建一个文件夹files
,然后在视图中添加视图:
from .models import Article
class ArticleView(View):
def get(self,request):
return render(request,'file.html')
def post(self,request):
title = request.POST.get('title')
content = request.POST.get('content')
file = request.FILES.get('myfile')
Article.objects.create(title=title,content=content,file=file)
return HttpResponse('success')
这里我们继续渲染的时上面那个示例的模板,所以我们需要在上面的模板中添加两个input标签,在file.html中的body中写入:
<form action="" method="post" enctype="multipart/form-data">
<input type="text" name="title"><br>
<input type="text" name="content"><br>
<input type="file" name="myfile"><br>
<input type="submit" value="提交"><br>
form>
然后我们在urls中添加映射,就能够正常的进行访问了。
注意: 在数据库中file字段保存的是文件的路径,而不是文件的信息。
上面我们是使用了upload_to来指定上传的文件的目录。我们也可以指定MEDIA_ROOT,就不需要在FielField中指定upload_to,他会自动的将文件上传到MEDIA_ROOT的目录下。
在settingsz.py中最下面添加代码:
# 指定上传的文件存放位置
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
# 获取上传的文件的url
MEDIA_URL = '/media/'
然后我们将上面的files文件夹修改一个文件名为media
。并且可以将file字段中的upload_to参数也去掉。
这样,我们也能将文件上传至我们指定的media目录下。
但是,这样就产生了一个问题,随着网站越来越大,那么文件上传的次数肯定也多了,全部都放在media目录下不方便我们管理。所以者恶搞时候我们可以对文件存储的位置进一步划分。
我们在file字段中继续传入一个参数upload_to
file = models.FileField(upload_to='files')
这样,因为我们在settings中设置了MEDIA_ROOT参数,所以Django会先去查找MEDIA_ROOT参数中的值,然后再寻找upload_to中的值,所以最后上传的文件的路径为media/files/< filename >
。
当然我们也可以按照时间来进行储存,只需要改变upload_to的值就行了。
file = models.FileField(upload_to='%Y/%m/%d/')
然后就会在media下生成一个年/月/日
的文件夹。并且将我们上传的文件放入里面。
如果我们想要访问上传的文件,那么我们直接输入网址
127.0.0.1:8000/media/
是访问不到的,那么我们应该怎样来进行访问呢。
这个时候我们就需要使用到MEDIA_URL
了。
在主urls中添加代码
from django.urls import path,include
from django.conf.urls.static import static
# 导入settings文件,就能得到settings中的所有东西
from django.conf import settings
urlpatterns = [
path('front/', include('front.urls')),
path('file/',include('file.urls')),
] + static(settings.MEDIA_URL,document_root = settings.MEDIA_ROOT)
这样,我们就可以像上面那样输入网址对文件进行访问了。
因为static函数返回的是一个列表,所以我们用+
对他们进行连接,就会把static返回的列表中的值放入urlpatterns这个列表中。如果文件时图片也是一样可以访问的。
为什么我们需要限制上传文件的扩展名呢,因为如果别人上传一些后缀名为.py、.php
等文件,那么这些文件是可以被运行的,那么就对我们的网站存在着极大的安全隐患。所以我们需要限制文件上传的后缀名。
如果想要限制上传的文件的拓展名,那么我们就需要用到表单来进行限制。我们可以使用普通的Form表单,也可以使用ModelForm,直接从模型中读取字段。
首先修改models中的代码,给file字段添加一个validators
from django.db import models
from django.core import validators
# Create your models here.
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
# 只允许上传txt文件
file = models.FileField(upload_to='%Y/%m/%d/',validators=[validators.FileExtensionValidator(['txt'],message='file必须为txt文件')])
然后我们使用modelForm对数据进行验证,新建一个forms.py文件,写入代码:
from django import forms
from .models import Article
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = '__all__'
然后修改Views中的ArticleView视图,将里面的post函数注释掉,然后重新写入一个post函数
def post(self,request):
# request.POST,对普通字段进行验证
# request.FILES 对文件字段进行验证
form = ArticleForm(request.POST,request.FILES)
if form.is_valid():
form.save()
return HttpResponse('success')
else:
print(form.errors.get_json_data())
return HttpResponse('fail')
因为我们使用的是modelForm,所以前端传入数据的name属性一定要和model中的字段名一样。所以我们修改file.html中的代码
<form action="" method="post" enctype="multipart/form-data">
<input type="text" name="title"><br>
<input type="text" name="content"><br>
{#
#}
<input type="file" name="file"><br>
<input type="submit" value="提交"><br>
</form>
这样,就完成了我们的代码,就可以输入网址进行测试效果了。
上传图片跟上传普通文件是一样的。只不过是上传图片的时候Django会判断上传的文件是否是图片的格式(除了判断后缀名,还会判断是否是可用的图片)。如果不是,那么就会验证失败。
这里我们就修改models中的字段就行了,就不去添加新的字段了。
models中修改代码:
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
# 只允许上传txt文件
# file = models.FileField(upload_to='%Y/%m/%d/',validators=[validators.FileExtensionValidator(['txt'],message='file必须为txt文件')])
# 上传图片
file = models.ImageField(upload_to='%Y/%m/%d/')
forms中修改代码:
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = '__all__'
error_messages = {
'file':{
'invalid_image':'请上传正确格式的图片~~',
},
}
然后就能对图片进行上传了,ImageField字段在底层会帮我们进行验证是否为可用的图片,如果不是,就会返回错误。
注意: 使用ImageField,必须要先安装Pillow库:pip install pillow,如果安装了pillow库,仍然有错误的话,应该就是pillow库版本过低,可以安装指定的最新版本:
目前我的最新版本为5.3.0。可以输入以下命令安装
pip install pillow==5.3.0