十、Django的文件上传

一、上传文件相关

  1. 请求头ContentType

ContentType指的是请求体的编码类型,常见的类型共有3种:

  1. application/x-www-form-urlencoded
    最常见的 POST 提交数据的方式了
    浏览器的原生 form 表单,如果不设置 enctype 属性,那么最终就会以 默认格式application/x-www-form-urlencoded 方式提交数据,ajax默认也是这个
    urlencoded是一种数据格式,
    比如:
    username=yang&password=123&csrfmiddlewaretoken=.......
    # 这样的一种格式,类似get请求提交数据的格式

    这样在后台,就可以通过request.POST,就可以直接拿到数据了

    QueryDict:{'username':['yang']},'password':['123'],......

  2. multipart/form-data
    专门用来传输文件的消息格式,会把文件作为片段数据发送,而不是整个文件一起发送

  3. application/json
    告诉服务端消息主体是序列化后的 JSON 字符串
    之前给ajax传递数据,就是把字典写成了一个类似json类型的数据
    def login(request):
    ...
    ret = '{"code":0,"redirect_url":"/index/"}'
    return HttpResponse(ret)

    在不加content_type='application/json'时,传递过去的就是一个字符串
    def login(request):
    d1 = {"code":0,"redirect_url":"/index/"}
    import json
    d1_json = json.dumps(d1)
    return HttpResponse(d1_json,)

    success:function (res) {
    console.log(res,typeof res);
    # {"code": 0, "redirect_url": "/index/"} string
    ....
    }

    这个时候,就不方便我们后续的使用,我们期望还是能拿到一个字典(自定义对象的类型)

    使用content_type='application/json',ajax就会调用自己内部的反序列化机制,直接调用反序列化方法
    def login(request):
    d1 = {"code":0,"redirect_url":"/index/"}
    import json
    d1_json = json.dumps(d1)
    return HttpResponse(d1_json,content_type='application/json')

    success:function (res) {
    console.log(res,typeof res);
    # {code: 0, redirect_url: "/index/"} "object"
    ....
    }

    此时,ajax端不需要自己序列化,也拿到了object类型的对象,这是因为内部自动调用的

  4. application/json 在ajax端发送数据,我们如何在视图函数中应用

    可见,ajax通过异步请求,在data路径拿到了数据组成了li标签,然后放在了ul标签下。

    自动拼接content_type='application/json',可以从网页的Network下的XHR观察出来,XHR下的请求就是ajax请求

    1. form表单上传文件

    2. 配置路径 url(r'^upload/', views.upload,name='upload'),

    3. 写html,就一个表单提交数据,文件提交的(input属性是type="file")


      {% csrf_token %}
      头像:
      用户名:

    4. 写视图函数
      def upload(request):
      if request.method == 'GET':
      return render(request,'upload.html')
      else:
      print(request.POST)
      # 拿到的是post请求的数据,文件相关数据去request.FILES拿
      return HttpResponse('ok')

      打印的request.POST,数据如下:
      <QueryDict: {'csrfmiddlewaretoken': ['XR5FUZ8nf9KPo8uNzt8OD9q0OKmeVfuLEDVDyDlTMCrWtX0jvZ6gZcwwopucWQo8'], 'head_pic': ['111.png'], 'username': ['yangzm']}>
      从得到的数据可以发现,request.POST得到的文件数据就是一个名字,没有任何意义

    5. 文件的数据,都在request.FILES中
      # 在form表单里,要先指定消息格式
      # 默认的请求消息格式是urlencoded,携带不了文件数据
      # 要指定multipart/form-data才可以携带数据


      ......

      def upload(request):
      if request.method == 'GET':
      return render(request,'upload.html')
      else:
      print(request.FILES)
      return HttpResponse('ok')

      如果form表单没有指定文件的消息格式,接收不到数据,就得到一个空字典:

      <MultiValueDict: {}>

      此时再打印request.FILES就拿到了上传了文件的数据:

      <MultiValueDict: {'head_pic': [<InMemoryUploadedFile: 111.png (image/png)>]}>

    6. 把得到的文件数据读取出来,并保存到本地
      def upload(request):
      if request.method == 'GET':
      return render(request,'upload.html')
      else:
      file_obj = request.FILES.get('head_pic')
      file_name = file_obj.name

      with open(file_name,'wb') as f:
      for i in file_obj:
      f.write(i)
      return HttpResponse('ok')

    7. 以上方法上传的文件没有指定路径,就直接保存在了项目的文件夹里;此时想要把上传的文件保存在指定的文件夹中,比如项目下的statics中的img文件夹中
      from ajax_pro import settings
      import os

      def upload(request):
      if request.method == 'GET':
      return render(request,'upload.html')
      else:

      file_obj = request.FILES.get('head_pic')
      file_name = file_obj.name

      path = os.path.join(settings.BASE_DIR,'statics','img',file_name)
      # 拼接路径
      with open(path,'wb') as f:
      for i in file_obj:
      f.write(i)
      # 每次读的文件不是固定长度,每次读一行,识别符为\r,\n,,\r\n,遇到这几个符号就算是读了一行

      return HttpResponse('ok')

    8. 另一种保存文件的方法:
      from ajax_pro import settings
      import os

      def upload(request):
      if request.method == 'GET':
      return render(request,'upload.html')
      else:

      file_obj = request.FILES.get('head_pic')
      file_name = file_obj.name

      path = os.path.join(settings.BASE_DIR,'statics','img',file_name)
      # 拼接路径
      with open(path,'wb') as f:
      for chunck in file_obj.chunks():
      f.write(chunck)
      # 固定长度读取,默认一次读65536B,也就是64KB,2.5M,是一个生成器

      return HttpResponse('ok')

    9. ajax上传文件

    如何获取当前文件对象的数据:$('#file')[0].files[0]

    后端的读取文件并保存的代码跟form的文件上传一样

    前端的html文件,用到ajax

    ajax上传头像:
    ajax用户名:

    {% csrf_token %}


    根据以前写ajax代码的习惯,会这样写,但是会报错,因为文件上传要依靠FormData这个工具

    正确写法:

    ajax上传头像:
    ajax用户名:

    {% csrf_token %}


    注意点:

    • ajax文件上传需要借助FormData,给它填键值对的方式传输数据
    • data直接传formdata,但是后面要加processData:false和contentType:false
    1. JSON 机制
    • JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation)
    • JSON 是轻量级的文本数据交换格式
    • JSON 独立于语言 *
    • JSON 具有自我描述性,更易理解

    json和python数据类型对比:

    但是,json在转换时间类型的时候,会出现问题:

    import datetime
    import json

    d = {'name':'yang','birthday':datetime.datetime.now()}
    json_str = json.dumps(d)

    print(json_str)

    报错,TypeError: Object of type 'datetime' is not JSON serializable,意思是'datetime'类型的是不能序列化的

    json序列化时间日期类型的数据的方法

    from datetime import date,datetime
    import json

    对含有日期格式数据的json数据进行转换

    class JsonCustomEncoder(json.JSONEncoder):
    # 序列化的功能就是json.JSONEncoder这个类做的,因为它不能序列化时间,所以继承他的情况下进行拓展
    def default(self, field):
    if isinstance(field,datetime):
    # 如果是datetime类型的,返回一个datetime类型的字符串格式化输出
    return field.strftime('%Y-%m-%d %H:%M:%S')
    elif isinstance(field,date):
    # 如果是date类型的,返回一个date类型的字符串格式化输出
    return field.strftime('%Y-%m-%d')
    else:
    # 如果不是date、datetime类型的,就用父类的方法
    return json.JSONEncoder.default(self,field)

    d1 = datetime.now()
    json_str = json.dumps(d1,cls=JsonCustomEncoder)
    print(json_str) # "2019-07-29 21:08:25"

posted on 2019-07-30 20:55  雨后清明  阅读(137)  评论(0)    收藏  举报

导航