深度解析Django文件上传与下载:附带多种上传方式的源码示例

深度解析Django文件上传与下载:附带多种上传方式的源码示例-山海云端论坛
深度解析Django文件上传与下载:附带多种上传方式的源码示例
此内容为付费阅读,请付费后查看
5积分
付费阅读
图片[1]-深度解析Django文件上传与下载:附带多种上传方式的源码示例-山海云端论坛

一、Django文件上传(File Uploads)

在文件上传过程中,实际文件数据存储在request.FILES中。每个条目都是UploadedFile对象,是对Python文件对象的简单封装,具有Django特定的功能。

UploadedFile对象的属性和方法:

  • name: 上传文件的名称
  • size: 上传文件的大小
  • content_type: 上传文件的content_type报头
  • charset: 文件编码
  • mode: 文件的读写模式
  • open([mode=None]): 打开或重新打开文件
  • read(): 读取整个上传文件的数据(慎用于大文件)
  • multiple_chunks(chunk_size=None): 判断文件是否足够大
  • chunks(chunk_size=None): 返回一个生成器对象,用于逐块读取文件内容

存储文件的两种方式:

1、将上传的文件存储在本地:

<code>f=request.FILES['image'] </code><code>with open('some/file/name.txt', 'wb+') as destination:</code><code> for chunk in f.chunks():</code><code> destination.write(chunk)</code>

2、手动存储:

<code>from django.core.files.base import ContentFile </code><code>photo=request.FILES.get('photo','') </code><code>if photo:</code><code> file_content = ContentFile(photo.read())</code><code> #创建File对象 </code><code>car.photo.save(photo.name, file_content)</code><code> #保存文件到car的photo域 </code><code>car.save()</code>

三、文件上传的几种方式:

  1. 简单文件上传实现: 利用Django实现文件上传并保存到指定路径下。需要注意表单的enctype应为multipart/form-data
<code><!DOCTYPE html> </code><code><head> </code><code><meta charset="UTF-8"></code><code> <title>uploadFile</title> </code><code></head> </code><code><body> </code><code><form method="post" action="" enctype="multipart/form-data"></code><code> {% csrf_token %} </code><code><label> 上传文件 </label> </code><code><input type="file" name="myfile" /> </code><code><br/> </code><code><input type="submit" value="upload"/> </code><code></form> </code><code></body> </code><code></html></code>

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

<code>from django.shortcuts import render </code><code>from django.http import HttpResponse </code><code>def upload_file(request): </code><code><em># 请求方法为POST时,进行处理; </em></code><code>if request.method == "POST":</code><code> <em># 获取上传的文件,如果没有文件,则默认为None;</em></code><code> File = request.FILES.get("myfile", None)</code><code> if File is None: </code><code>return HttpResponse("no files for upload!")</code><code> else: </code><code><em># 打开特定的文件进行二进制的写操作;</em></code><code> with open("/tmp/%s" % File.name, 'wb+') as f:</code><code> <em># 分块写入文件; </em></code><code>for chunk in File.chunks():</code><code> f.write(chunk) </code><code>return HttpResponse("upload over!") </code><code>else: </code><code>return render(request, 'upload.html')</code>

处理上传文件就是往服务器上生成一个文件,并将上传的文件内容写到新的文件中。然后写文件使用FILE.chunks()方法,而不是使用read()方法,能确保大文件并不会占用系统过多的内存。写url路由:
# /urls.py

<code>from django.conf.urls import url </code><code>from . import views </code><code>urlpatterns = [ </code><code>url(r'^$', views.index, name='index'), </code><code>url(r'^upload/$', views.upload_file, name='upload_file'), </code><code>]</code>

选择文件就可以上传

2、基于forms表单上传文件

在Django中我们可以采用Form类来处理表单,通过实例化处理和在模板中渲染,就可以轻松完成表单的需求。创建一个实例:
# forms.py

<code>from django import forms </code><code>class UploadFileForm(forms.Form): </code><code>title = forms.CharField(max_length=50) </code><code>file = forms.FileField()</code>

处理这个表单的视图会在request中接收到上传文件的数据。FILES是个字典,它包含每个FileField的键(或者ImageField,FileField的子类)。这样的话就可以用request.FILES[‘file’]来存放表单中的这些数据了。
注意request.FILES只有在请求方法为POST并且提交请求的<form>具有enctype=”multipart/form-data”属性时才包含数据。否则,request.FILES将为空。
视图:
#views.py

<code>from django.shortcuts import render </code><code>from django.http import HttpResponse </code><code>from .forms import UploadFileForm </code><code><br></code><code>def handle_upload_file(file):</code><code> with open("/tmp/%s" % file.name, 'wb+') as f:</code><code> for chunk in file.chunks(): </code><code>f.write(chunk) </code><code>def upload_file(request):</code><code> if request.method == "POST":</code><code> form = UploadFileForm(request.POST, request.FILES)</code><code> if form.is_valid():</code><code> handle_upload_file(request.FILES['file'])</code><code> #handle_upload_file(form.files['file']) </code><code>return HttpResponse('upload success!') </code><code>else: </code><code>              form = UploadFileForm()    </code><code>           return render(request, 'upload.html', {'form': form})</code>

处理上传文件就是往服务器上生成一个文件并将上传的文件内容写到新的文件中,handle_upload_file函数即接收上传文件对象为参数,然后本地打开一个文件,从上传的文件中读出文件,写入新的文件中。 

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

<code><!DOCTYPE html> </code><code><head> </code><code><meta charset="UTF-8"> </code><code><title>uploadFile</title> </code><code></head> </code><code><body> </code><code><form method="post" action="" enctype="multipart/form-data"></code><code> {% csrf_token %} </code><code>{{ form }} </code><code>     <input type="submit" value="upload"/>     </code><code></form> </code><code></body> </code><code></html></code>

表单被模板渲染后,会生成静态源码:

<code><label for="id_title">Title:</label> </code><code><input type="text" name="title" id="id_title" required="" maxlength="50"> </code><code><label for="id_file">File:</label> </code><code><input type="file" name="file" id="id_file" required=""></code>

3.通过ajax上传

前端代码

<code><div></code><code> <input type="file" name="file" id="file_upload"></code><code> <input type="button" value="上传" onclick="FileUpload()"></code><code></div></code><code><script src="/static/js/jquery-3.2.1.min.js"></script></code><code><script></code><code> function FileUpload() {</code><code> var form_data = new FormData();</code><code> var file_info =$( '#file_upload')[0].files[0];</code><code> form_data.append('file',file_info);</code><code> <em>//if(file_info==undefined)暂且不许要判断是否有附件</em></code><code> <em>//alert('你没有选择任何文件');</em></code><code> <em>//return false</em></code><code> $.ajax({</code><code> url:'/upload_ajax/',</code><code> type:'POST',</code><code> data: form_data,</code><code> processData: false, <em>// tell jquery not to process the data</em></code><code> contentType: false, <em>// tell jquery not to set contentType</em></code><code> success: function(callback) {</code><code><br></code><code> console.log('ok')</code><code> }</code><code> });</code><code><br></code><code> }</script></code>

#views.py

<code>import os</code><code><br></code><code>def upload_ajax(request):</code><code> if request.method == 'POST':</code><code>        file_obj = request.FILES.get('file')</code><code>        f = open(os.path.join(BASE_DIR, 'static', 'upload', file_obj.name), 'wb')</code><code> for chunk in file_obj.chunks():</code><code> f.write(chunk)</code><code>        f.close()</code><code> return HttpResponse('OK')</code>

4、 同时上传多个文件如果要使用一个表单字段同时上传多个文件,需要设置字段HTML标签的multiple属性为True,如下所示:# forms.py

<code>from django import forms </code><code>class FileFieldForm(forms.Form):</code><code> file_field = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))</code>

然后,自己编写一个FormView的子类,来处理多个文件上传:

# views.py

<code>from django.views.generic.edit import FormView </code><code>from .forms import FileFieldForm </code><code>class FileFieldView(FormView):</code><code> form_class = FileFieldForm </code><code>template_name = 'upload.html' # 用你的模版名替换. </code><code>success_url = '...' # 用你的URL或者reverse()替换. </code><code>def post(self, request, *args, **kwargs):</code><code> form_class = self.get_form_class() </code><code>form = self.get_form(form_class) </code><code>files = request.FILES.getlist('file_field')</code><code>             if form.is_valid():             </code><code>              for f in files:</code><code>              with each file.             </code><code>              return self.form_valid(form)         </code><code>              else:             </code><code>               return self.form_invalid(form)</code>

四、文件下载
1.简单文件下载:

<code>def download_file(request):</code><code> with open('/tmp/file_name.txt', 'rb') as f:</code><code>        c = f.read()     </code><code>        return HttpResponse(c)</code>

2.合理的文件下载:

<code>from django.http import StreamingHttpResponse </code><code>def download_file(request):</code><code> def file_iterator(file, chunk_size=512): </code><code>with open(file) as f: </code><code>while True: </code><code>c = f.read(chunk_size) </code><code>                   if c:                 </code><code> yield c </code><code>                   else:                  </code><code>                      break       </code><code>                   file = "file_name.txt"     </code><code>                   response = StreamingHttpResponse(file_iterator(file))     </code><code>                   return response</code>

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

<code>response['Content-Type'] = 'application/octet-stream'</code><code>response['Content-Disposition'] = 'attachment;filename="test.pdf"'</code>

完整代码如下:

<code>from django.http import StreamingHttpResponse </code><code>def download_file(request): </code><code>def file_iterator(file, chunk_size=512): </code><code>with open(file) as f: </code><code>while True: </code><code>c = f.read(chunk_size) </code><code>if c: </code><code>yield c </code><code>               else:                  </code><code>                   break       </code><code>               file = "big_file.pdf"     </code><code>               response = StreamingHttpResponse(file_iterator(file))     </code><code>               response['Content-Type'] = 'application/octet-stream'     </code><code>               response['Content-Disposition'] = 'attachment;filename="{0}"'.format(file)     </code><code>               return response</code>

五、Django 图片上传到数据库并调用显示完整示例

在models.py中,需要建立模型,这里使用了ImageField字段,用来存储图片路径,这个字段继承了FileField字段,本质上是一样的。这里Image.Field的默认max_length=100,我们可以根据需求自己指定。upload_to用于指定上传到哪个路径下。
使用ImageField首先需要装Pillow。pip install Pillow
#models.py

<code>class Test(models.Model): </code><code>name = models.CharField(max_length=50) </code><code>image = models.ImageField(upload_to='logo') </code><code>def __str__(self): </code><code>return self.name</code>

在settings.py中,设置MEDIA_URL和MEDIA_ROOT  

<code>MEDIA_URL = '/media/' </code><code>MEDIA_ROOT = os.path.join(BASE_DIR, 'media')</code>

需要告诉Django,媒体文件的位置在哪里。这样就和数据库存储的路径相对应,具体就是MEDIA_ROOT指定目录,upload_to就是在这个目录下进行操作。
1. 显示图片(图片调用)

<code>img = Test.objects.all()</code><code>return render(request, 'home.html', {'img':img})</code>

在视图函数中加入上面两句。在模板中,将图片展现出来: 

<code>{% for i in img %} </code><code><img src="{{ MEDIA_URL }}{{ i.image }}"> </code><code>{% endfor %}</code>

这里{{ MEDIA_URL }}是必须要的,因为数据库取出来的地址是/logo/img001.png这种,路径不完整,我们存储的路径上/media/logo/img001.png
2.  上传图片用户上传自己的头像,或者相册,这里做一个简单的示范:
首先需要一个form,enctype=”multipart/form-data” method=”post” 是必须要填写的,表示数据不经过编码,直接上传。{%csrf_token%}也是post时,django强制要求的。

<code><form enctype="multipart/form-data" action="#" method="post"> </code><code>{% csrf_token %}</code><code> <input type="text" name="name"> </code><code><input type="file" name="logo"> </code><code><input type="submit" value="upload"> </code><code></form></code>

#views.py

<code>if request.method == 'POST':</code><code>    file = request.FILES['logo']     </code><code>    if file:      </code><code>        new_img = Test(        </code><code>            name=request.POST.get('name'),             </code><code>            image=file          </code><code>            )         </code><code>        new_img.save()</code>

与普通的数据不同,这里使用了request.FILES字典的方式去获取文件,然后创建新的数据,并保存到数据库中。

© 版权声明
THE END
喜欢就支持一下吧
点赞7 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容