文件上传
我们经常需要在网站上上传各类文件,如办公文件(扩展名为.doc、.docx、.wps等)、图片文件(扩展名为.jpg、.png、.bmp等)、压缩文件(扩展名为.zip、.rar等)等,这些文件要上传到特定路径下。那么,文件上传究竟是如何实现的呢?下面我们通过一个具体的案例来介绍。
若要实现一次上传一个文件,需要经过建立上传模板、建立上传视图函数、建立调用上传模板的视图函数、设置上传文件视图函数路由、在浏览器上进行上传测试这5个步骤。
第一步:建立上传模板。
上传模板,就是为上传文件提供相应网页操作的界面,主要需要提供表单功能,对应 HTTP 请求POST提交功能,只有通过POST才能在线提交各种资源(这里指各种文件)。
首先,在项目模板路径下建立upload.html模板文件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>上传文件模板</title>
</head>
<body>
<form enctype="multipart/form-data" action="/uploadfile/" method="post">
{% csrf_token %}
<input type="file" name="newfile">
<br/>
<input type="submit" value="上传文件">
</form>
</body>
</html>
在上面的代码中,enctype属性用于挃定将上传文件数据发送给服务器端之前对表单数据进行编码的斱式,这里必须挃定multipart/form-data的值,表示不对字符进行编码,支持通过文件上传斱式将数据传输给服务器端。
action属性用于挃定需要上传的目标URL,这里挃定了/uploadfile/地址,要求在项目根路径下建立uploadfile子目录路径
method属性用于挃定post值(必须指定),表示用POST斱法提交表单资源给服务器端。
{%csrf_token%}表明在提交界面预防CSRF恶意攻击。作为一名合格的Web程序员,必须充分考虑网站安全问题,相关的内容我们将在第16章介绍。
第二步:建立上传视图函数。
通过模板提交文件并通过路由列表触发上传视图函数,便于将上传的文件保存到指定路径下。我们要在根文件views.py中建立上传视图函数。
from django.http import HttpResponse,HttpResponseRedirect
from django.shortcuts import render,redirect
import os
BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
def upload_file(request):
if request.method == "POST":
newfile=request.FILES.get('newfile',None)
if not newfile:
return HttpResponse('提交无效,没有文件上传!')
to_path=open(os.path.join(BASE_DIR,'uploadfile',newfile.name),'wb+')
for chunk in newfile.chunks():
to_path.write(chunk)
to_path.close()
return HttpResponse('上传成功!')
else:
return HttpResponse('非表单提交访问!')
注意
上述代码中有以下4点注意事项。
1.FILES.get()方法获取的是表单input组件name传递过来的文件信息,因此其中的第一个参数"newfile"的值必须和name的值一致;这里的FILES是一个字典类型对象,保存了上传文件的信息,get()方法用来获取字典中键对应的值。
2.通过POST将文件上传到指定地址的Web网站上,该文件是临时存储在服务器内存上的,因此,需要将指定地址的文件迚一步存储到服务器端指定的硬盘地址上。把上传的文件以二迚制流的形式写入指定硬盘地址才算完成了永久性的硬盘存储。这里指定的路径(含文件名)是“项目根路径\uploadfile\上传文件名称”,由join()方法拼接实现。
3.用chunks()对文件迚行分块处理,把分块内容写入指定的存储地址,这里考虑了大容量文件上传要求。
4.在实际运行环境下,强烈建议将上传地址指定为Web项目路径以外的其他物理路径,如D:\upload,以确保上传文件不被执行(预防恶意代码攻击)。
第三步:建立调用上传模板的视图函数。
在根文件views.py中定义网页访问时要调用上传模板的视图函数。
def login1(request):
return render(request,'upload.html')
第四步:设置上传文件视图函数路由。
在根路由文件urls.py中设置如下的路由。
path('file/',views.login1),
path('uploadfile/',views.upload_file),
第五步:在浏览器上进行上传测试。
这个读者自行测试,如有问题欢迎评论一起讨论!!
文件下载
网站支持上传文件,自然也需要支持下载文件,这样可以为访问者提供更多的在线资料。Django提供了HttpResponse、StreamingHttpResponse、FileResponse、JsonResponse这4种下载文件的斱式。
● HttpResponse对象是最基础的文件下载支持对象。
● StreamingHttpResponse 对象是以文件字节流的形式传递并下载文件的,适应性更加广泛,可以支持大规模下载数据和文件。
● FileResponse 对象继承自 StreamingHttpResponse 对象,采用文件流形式传输,只支持文件的下载。
● JsonResponse对象继承自HttpResponse对象,用于处理JSON编码格式的文件并提供下载支持。
下载文件涉及建立下载文件子路径、建立下载模板、建立下载视图函数、设置下载文件视图函数路由、在浏览器端进行下载测试这几个步骤,下面我们通过一个案例来具体说明
第一步:建立下载文件子路径。
建立下载文件子路径“download”,在其中存放一个文件。
第二步:建立下载模板。
在根路径的模板子路径下建立download.html下载模板,在<body>标签中增加下载界面功能。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>下载模板</title>
</head>
<body>
<br />
<div class="col-md-4">
<a href="{% url 'download' %}" rel="external nofollow">
单击下载文件
</a>
</div>
</body>
</html>
其中,href组件用于在界面中生成下载链接。路由命名download变量通过下载视图路由和函数获取下载文件的地址和名称。
第三步:建立下载视图函数。
在fruits.py应用的views.py文件中增加下载视图函数。
from django.http import HttpResponse,Http404
import os
BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
def download(request):
filename='456.png'
try:
download_path=open(os.path.join(BASE_DIR,'download',filename),'rb')
d=HttpResponse(download_path)
d['content_type']='application/octet-stream'
d['Content-Disposition']='attachment;filename='+filename
return d
except:
raise Http404('下载文件'+filename+'失败!')
在上述代码中,content_type挃定的值application/octet-stream的意思为,下载内容以字节流形式被保存到挃定地址的文件中。
在Content-Disposition挃定的值中,attachment为下载文件模式,filename为下载文件名,该返回信息会被模板href组件接受。
第四步:设置下载文件视图函数路由。
在根路由文件urls.py中设置跳转到fruits应用的路由。
path('file1/',include(('fruits.urls','index'),namespace='index')),
在fruits应用的urls.py文件中设置如下的路由。
path('d/',views.index,name='index'),
path('download/file1',views.download,name='download'),
其中第二行path的第一个参数必须是二级子路径,如download/file1,否则浏览器访问时会报出“下载地址出错”的错误。
第五步:在浏览器端进行下载测试。
在命令提示符中启动Web服务器,然后在浏览器的地址栏中输入127.0.0.1:8000/file1/d并按下回车键,接着选择“单击下载文件”
读者自行测试,有问题留言一起讨论
【完】