• 进入"运维那点事"后,希望您第一件事就是阅读“关于”栏目,仔细阅读“关于Ctrl+c问题”,不希望误会!

Django上传下载文件

Python框架 彭东稳 6年前 (2017-11-25) 30410次浏览 已收录 2个评论

一、简单文件上传实现

利用Django实现文件上传并且保存到指定路径下,其实并不困难,可以不需要用到django的forms,也不需要django的models,就可以简单实现上传功能。下面简单实现一下。

当Django在处理文件上传的时候,文件数据被保存在request.FILES。需要特别注意的是,只有当request方法是POST,且发送request的<form>有属性enctype=”multipart/form-data”时,表明不对字符进行编码,request.FILES中才会包含文件数据,否则request.FILES为空。

比如先写upload.html前面上传页面:

然后写一个upload_file视图函数,处理文件上传,代码如下:

处理上传文件就是往服务器上生成一个文件,并将上传的文件内容写到新的文件中。然后写文件使用FILE.chunks()方法,而不是使用read()方法,能确保大文件并不会占用系统过多的内存。FILE方法和属性下面介绍。

最后写一个url匹配就可以了:

最终效果如下图,选择文件就可以上传了:

Django上传下载文件

那么这整个具体流程就是,访问http://10.10.0.109:8000/polls/upload/,调用upload_file函数,由于是GET请求,所以直接返回了upload.html页面,然后就可以进行POST请求上传文件了。

二、基于表单上传文件

在Django中我们可以采用Form类来处理表单,通过实例化处理和在模板中渲染,就可以轻松完成表单的需求,采用django的表单处理方式,能帮我们省去很多的工作,比如验证不能为空,或者要符合某种模式的输入才有效,这些处理起来非常方便,不用自己再单独写代码去验证表单的数据正确性,所以在开发中比较常用,Form提供了很多表单字段,比如日期,文本类型等,如果你熟悉基本的html,学起来会非常容易上手,下面只说下表单文件的上传,因为这个类型比较特殊,需要一点特殊的处理,我们来创建一个简单的一个实例:

这个表单就2个字段,要求用户title和上传一个文件或图片。

处理这个表单的视图会在request中接收到上传文件的数据。FILES是个字典,它包含每个FileField的键(或者ImageField,FileField的子类)。这样的话就可以用request.FILES[‘file’]来存放表单中的这些数据了。

注意request.FILES只有在请求方法为POST并且提交请求的<form>具有enctype=”multipart/form-data”属性时才包含数据。否则,request.FILES将为空。

大多数时候,你将只是简单地从request向表单中传递数据,就像Binding uploaded files to a form描述的那样。所以我们的视图处理逻辑基本类似下面这样:

请注意,我们必须将request.FILES传递到form的构造函数中;这就是文件数据如何绑定到一个表单中。

这个函数判断用户的是否为POST请求,如果是并验证是有效的,然后就返回OK,在验证正确和返回OK的中间放我们的上传文件处理函数handle_upload_file,因为只有文件上传成功能返回OK。然后给这个handle_upload_file函数传递一个“request.FILES[‘file’]”,就是我们获取到的文件;也可以从表单中获取到,比如使用form提供的files或cleaned_data属性(form.files[‘file’]),这是表单提供的属性。如果是GET请求,就直接显示一个空表单,让用户输入。

注意我们必须向表单的构造器中传递request.FILES,这是文件数据绑定到表单的方法。FILES中的每个键为<input type=”file” name=”” />中的name。

然后写handle_upload_file函数,处理上传文件就是往服务器上生成一个文件,并将上传的文件内容写到新的文件中,所以它的基本函数是这样的,接收上传文件对象为参数,然后本地打开一个文件,从上传的文件中读出文件,写入新的文件中,代码如下:

接下来我们把form放到模板中去渲染,模板代码大概如下:

这个表单被模板渲染后,看上去应该像下面这样,会生成静态源码:

这样就完成了一个文件的上传,完毕。

上传数据在哪里储存?

在你保存上传文件之前,数据需要储存在某个地方。

通常,如果上传文件小于2.5MB,Django会把整个内容存到内存。这意味着,文件的保存仅仅涉及到从内存读取和写到磁盘,所以非常快。

但是,如果上传的文件很大,Django会把它写入一个临时文件,储存在你系统的临时目录中。在类Unix的平台下,你可以认为Django生成了一个文件,名称类似于/tmp/tmpzfp6I6.upload。如果上传的文件足够大,你可以观察到文件大小的增长,由于Django向磁盘写入数据。

这些特定值 – 2.5 MB,/tmp,以及其它 — 都仅仅是”合理的默认值”,它们可以自定义,这会在下一节中描述。

UploadedFile对象

在文件上传期间,实际文件数据存储在request.FILES中。此字典中的每个条目都是UploadedFile对象(或子类) – 上传文件的简单包装器。UploadedFile对象是对Python file对象的一个简单封装,并带有Django特定的附加功能。需要表示文件的时候,Django内部会使用这个类。UploadedFile对象拥有下列属性和方法:

name

含有MEDIA_ROOT相对路径的文件名称。

size

文件的字节数。

mode

文件的读写模式。

open([mode=None])

打开或者重新打开文件(同时会执行File.seek(0))。 mode参数的值和Python内建的open()相同。

重新打开一个文件时,无论文件原先以什么模式打开,mode都会覆盖;None的意思是以原先的模式重新打开。

read([num_bytes=None])

读取文件内容。可选的size参数是要读的字节数;没有指定的话,文件会一直读到结尾。

__iter__()

迭代整个文件,并且每次生成一行。

File现在使用通用的换行符。以下字符会识别为换行符:Unix换行符’\n’,WIndows换行符’\r\n’,以及Macintosh旧式换行符\r。

chunks([chunk_size=None])

迭代整个文件,并生成指定大小的一部分内容。chunk_size默认为64KB。

处理大文件时这会非常有用,因为这样可以把他们从磁盘中读取出来,而避免将整个文件存到内存中。

multiple_chunks([chunk_size=None])

如果文件足够大,需要按照提供的chunk_size切分成几个部分来访问到所有内容,则返回True 。

write([content])

将指定的内容字符串写到文件。取决于底层的储存系统,写入的内容在调用close()之前可能不会完全提交。

close()

关闭文件。

除了这些列出的方法,File沿用了file对象的以下属性和方法:encoding、fileno、flush、isatty、newlines、read、readinto、readlines、seek、softspace、tell、truncate、writelines、xreadlines。

基于这些属性和方法,我们把代码很轻松写的更规范,比如限制上传文件大小等。

三、文件下载

基于Django建立的网站,如果提供文件下载功能,最简单的方式莫过于将静态文件交给Nginx等处理,但有些时候,由于网站本身逻辑,需要通过Django提供下载功能,如页面数据导出功能(下载动态生成的文件)、先检查用户权限再下载文件等。因此,有必要研究一下文件下载功能在Django中的实现。

最简单的文件下载功能的实现

将文件流放入HttpResponse对象即可,如:

这种方式简单粗暴,适合小文件的下载,但如果这个文件非常大,这种方式会占用大量的内存,甚至导致服务器崩溃。

更合理的文件下载功能

Django的HttpResponse对象允许将迭代器作为传入参数,将上面代码中的传入参数c换成一个迭代器,便可以将上述下载功能优化为对大小文件均适合;而Django更进一步,推荐使用 StreamingHttpResponse对象取代HttpResponse对象,StreamingHttpResponse对象用于将文件流发送给浏览器,与HttpResponse对象非常相似,对于文件下载功能,使用StreamingHttpResponse对象更合理。

因此,更加合理的文件下载功能,应该先写一个迭代器,用于处理文件,然后将这个迭代器作为参数传递给StreaminghttpResponse对象,如:

文件下载功能再次优化

上述的代码,已经完成了将服务器上的文件,通过文件流传输到浏览器,但文件流通常会以乱码形式显示到浏览器中,而非下载到硬盘上,因此,还要在做点优化,让文件流写入硬盘。优化很简单,给StreamingHttpResponse对象的Content-Type和Content-Disposition字段赋下面的值即可,如:

所以,完整代码如下:

参考:Django文件上传


如果您觉得本站对你有帮助,那么可以支付宝扫码捐助以帮助本站更好地发展,在此谢过。
喜欢 (11)
[资助本站您就扫码 谢谢]
分享 (0)

您必须 登录 才能发表评论!

(2)个小伙伴在吐槽
  1. 82c42f5f29321c14070e346cbccdcdf3 第三代
    guestbook2018-02-26 03:05 Windows 7 | Chrome 45.0.2454.101
  2. class UploadFileForm(forms.Form): title = forms.CharField(max_length=50) file = forms.FileField() 这儿的title有什么用?我发现怎么填写对后台没有影响
    leeforget2018-02-23 11:15 Windows 10 | Chrome 58.0.3029.110