前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Django之文件上传下载

Django之文件上传下载

作者头像
菲宇
发布2022-05-06 13:31:10
3.1K0
发布2022-05-06 13:31:10
举报
文章被收录于专栏:菲宇菲宇

一、Django处理文件上传File Uploads

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

代码语言:javascript
复制
HttpRequest.FILES  表单上传的文件对象存储在类字典对象request.FILES中,表单格式需为multipart/form-data 
<form enctype="multipart/form-data" method="post" action="/foo/">
<input type="file" name="image" />
request.FILES中的键来自于表单中的<input type="file" name="" />的name值:
img=request.FILES['image']
request.FILES中的值均为UploadedFile类文件对象。
UploadedFile对象
UploadedFile是类文件对象,具有以下方法和属性:
UploadedFile.read()读取整个上传文件的数据,文件较大时慎用。
UploadedFile.multiple_chunks(chunk_size=None)判断文件是否足够大,一般为2.5M
UploadedFile.chunks(chunk_size=None)返回一个生成器对象,当multiple_chunks()为True时应该使用这个方法来代替read().
UploadedFile.name上传文件的name。
UploadedFile.size传文件的大小。
UploadedFile.content_type 上传文件时的content_type报头,例如(e.g. text/plain or application/pdf).
UpladedFile.charset编码

UpladedFile.mode文件的读写模式。
UpladedFile.open([mode=None])打开或者重新打开文件(同时会执行File.seek(0))。 mode参数的值和Python内建的open()相同。重新打开一个文件时,无论文件原先以什么模式打开,mode都会覆盖;None的意思是以原先的模式重新打开。
UpladedFile.write([content])将指定的内容字符串写到文件。取决于底层的储存系统,写入的内容在调用close()之前可能不会完全提交。
close()关闭文件。

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

二、存储文件

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

代码语言:javascript
复制
f=request.FILES['image']
with open('some/file/name.txt', 'wb+') as destination:
    for chunk in f.chunks():
        destination.write(chunk)

2、手动存储:

代码语言:javascript
复制
from django.core.files.base import ContentFile
photo=request.FILES.get('photo','')
if photo:  
    file_content = ContentFile(photo.read())   #创建File对象
    car.photo.save(photo.name, file_content)   #保存文件到car的photo域
    car.save()

三、简单文件上传实现

利用Django实现文件上传并且保存到指定路径下,其实并不困难,可以不需要用到django的forms,也不需要django的models,就可以简单实现上传功能。下面简单实现一下。 当Django在处理文件上传的时候,文件数据被保存在request.FILES。需要特别注意的是,只有当request方法是POST,且发送request的<form>有属性enctype=”multipart/form-data”时,表明不对字符进行编码,request.FILES中才会包含文件数据,否则request.FILES为空。 比如先写upload.html前台上传页面: # ./polls/templates/polls/upload.html

代码语言:javascript
复制
<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <title>uploadFile</title>
</head>
<body>
    <form method="post" action="" enctype="multipart/form-data">
    {% csrf_token %}
       <label> 上传文件 </label>
       <input type="file" name="myfile" />
       <br/>
       <input type="submit" value="upload"/>
    </form>
</body>
</html>

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

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

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

代码语言:javascript
复制
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^polls/', include('polls.urls'))
]

# ./polls/urls.py

代码语言:javascript
复制
from django.conf.urls import url
from . import views
app_name = 'polls'
 
urlpatterns = [
    url(r'^$', views.index, name='index'),
    url(r'^upload/$', views.upload_file, name='upload_file'),
]

选择文件就可以上传了

四、基于表单上传文件

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

创建一个实例: # ./polls/forms.py

代码语言:javascript
复制
from django import forms
 
class UploadFileForm(forms.Form):
    title = forms.CharField(max_length=50)
    file = forms.FileField()

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

代码语言:javascript
复制
from django.shortcuts import render
from django.http import HttpResponse
from polls.forms import UploadFileForm
 
def upload_file(request):
    if request.method == "POST":
        form = UploadFileForm(request.POST, request.FILES)
        if form.is_valid():
            handle_upload_file(request.FILES['file'])
            #handle_upload_file(form.files['file'])
            return HttpResponse('upload success!')
    else:
        form = UploadFileForm()
    return render(request, 'polls/upload.html', {'form': form})

这个函数判断用户的是否为POST请求,如果是并验证是有效的,然后就返回OK,在验证正确和返回OK的中间放我们的上传文件处理函数handle_upload_file,因为只有文件上传成功能返回OK。然后给这个handle_upload_file函数传递一个“request.FILES[‘file’]”,就是我们获取到的文件;也可以从表单中获取到,比如使用form提供的files或cleaned_data属性(form.files[‘file’]),这是表单提供的属性。如果是GET请求,就直接显示一个空表单,让用户输入。 然后写handle_upload_file函数,处理上传文件就是往服务器上生成一个文件,并将上传的文件内容写到新的文件中,所以它的基本函数是这样的,接收上传文件对象为参数,然后本地打开一个文件,从上传的文件中读出文件,写入新的文件中,代码如下:  

代码语言:javascript
复制
def handle_upload_file(file):
    with open("/tmp/%s" % file.name, 'wb+') as f:
        for chunk in file.chunks():
            f.write(chunk)

接下来把form放到模板中去渲染,模板如下:

# polls/templates/polls/upload.html

代码语言:javascript
复制
<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <title>uploadFile</title>
</head>
<body>
    <form method="post" action="" enctype="multipart/form-data">
        {% csrf_token %}
        {{ form }}
       <input type="submit" value="upload"/>
    </form>
</body>
</html>

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

代码语言:javascript
复制
<label for="id_title">Title:</label>
<input type="text" name="title" id="id_title" required="" maxlength="50">
<label for="id_file">File:</label>
<input type="file" name="file" id="id_file" required="">

五、 同时上传多个文件

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

# forms.py

代码语言:javascript
复制
from django import forms

class FileFieldForm(forms.Form):
    file_field = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))

然后,自己编写一个FormView的子类,并覆盖它的post方法,来处理多个文件上传:

代码语言:javascript
复制
# views.py
代码语言:javascript
复制
from django.views.generic.edit import FormView
from .forms import FileFieldForm

class FileFieldView(FormView):
    form_class = FileFieldForm
    template_name = 'upload.html'  # 用你的模版名替换.
    success_url = '...'  # 用你的URL或者reverse()替换.

    def post(self, request, *args, **kwargs):
        form_class = self.get_form_class()
        form = self.get_form(form_class)
        files = request.FILES.getlist('file_field')
        if form.is_valid():
            for f in files:
                ...  # Do something with each file.
            return self.form_valid(form)
        else:
            return self.form_invalid(form)

六、文件下载

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

代码语言:javascript
复制
def download_file(request):
    # do something...
    with open('/tmp/file_name.txt', 'rb') as f:
        c = f.read()
    return HttpResponse(c)

这种方式简单粗暴,适合小文件的下载,但如果这个文件非常大,这种方式会占用大量的内存,甚至导致服务器崩溃。 更合理的文件下载功能 Django的HttpResponse对象允许将迭代器作为传入参数,将上面代码中的传入参数c换成一个迭代器,便可以将上述下载功能优化为对大小文件均适合;而Django更进一步,推荐使用 StreamingHttpResponse对象取代HttpResponse对象,StreamingHttpResponse对象用于将文件流发送给浏览器,与HttpResponse对象非常相似,对于文件下载功能,使用StreamingHttpResponse对象更合理。 因此,更加合理的文件下载功能,应该先写一个迭代器,用于处理文件,然后将这个迭代器作为参数传递给StreaminghttpResponse对象,如:   

代码语言:javascript
复制
from django.http import StreamingHttpResponse

def download_file(request):
    def file_iterator(file, chunk_size=512):
        with open(file) as f:
            while True:
                c = f.read(chunk_size)
                if c:
                    yield c
                else:
                    break
 
    file = "file_name.txt"
    response = StreamingHttpResponse(file_iterator(file))
    return response

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

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

七、示例:Django 图片上传到数据库并调用显示

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

代码语言:javascript
复制
class Test(models.Model):
    name = models.CharField(max_length=50)
    image = models.ImageField(upload_to='logo')
    def __str__(self):
         return self.name

建立好模型,需要进行迁移操作,

代码语言:javascript
复制
python manage.py makemigrations  
python manage.py migrate

在settings.py中,设置MEDIA_URL和MEDIA_ROOT   

代码语言:javascript
复制
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

需要告诉Django,媒体文件的位置在哪里。这样就和数据库存储的路径相对应了,具体就是MEDIA_ROOT指定目录,upload_to就是在这个目录下进行操作。 1. 显示图片(图片调用)   为了能够方便录入数据,我们使用django后台管理,创建管理页面。  python manage.py createsuperuser    根据提示进行创建。在app下admin.py中将需要上面创建的模型进行添加。 admin.site.register(Test)    开启runserver,打开admin页面,就可以创建具体的数据了,将图片进行添加。   我们需要调用的话,需要在view.py中将数据进行传递。 img = Test.objects.all() return render(request, 'home.html', {'img':img})    在视图函数中加入,上面两句。在模板中,将图片展现出来:  

代码语言:javascript
复制
{% for i in img %}
     <img src="{{ MEDIA_URL }}{{ i.image }}">
{% endfor %}

  这里{{ MEDIA_URL }}是必须要的,因为数据库取出来的地址是/logo/img001.png这种,路径不完整,我们存储的路径上/media/logo/img001.png    但到这里还是不能正常显示图片,会显示404,是因为图片的url也是需要通过django进行指派,我们还需要在urls.py进行设定。为了html模板能正确找到MEDIA_URL,TEMPLATES中导入相关的包。   

代码语言:javascript
复制
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')]
        ,
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
                'django.template.context_processors.media',#### add here
            ],
        },
    },
]

2.  上传图片 用户上传自己的头像,或者相册,这里做一个简单的示范:   首先需要一个form,enctype="multipart/form-data" method="post" 是必须要填写的,表示数据不经过编码,直接上传。{%csrf_token%}也是post时,django强制要求的。

代码语言:javascript
复制
<form enctype="multipart/form-data" action="#" method="post">
    {% csrf_token %}
    <input type="text" name="name">
    <input type="file" name="logo"> 
    <input type="submit" value="upload">
</form>

views.py

代码语言:javascript
复制
if request.method == 'POST':
    file = request.FILES['logo']
    if file:
        new_img = Test(
            name=request.POST.get('name'),
            image=file 
        )
        new_img.save()

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-05-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、Django处理文件上传File Uploads
  • 二、存储文件
  • 三、简单文件上传实现
  • 四、基于表单上传文件
    • 五、 同时上传多个文件
    • 六、文件下载
    • 七、示例:Django 图片上传到数据库并调用显示
    相关产品与服务
    对象存储
    对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档