Django框架—Ajax请求

一、json回顾

1.什么是json

json(Javascript Obiect Notation,JS对象标记)是一种轻量级的数据交换格式。

json是基于 ECMAScript (w3c制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。

  • 简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。

  • 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。

json本质是从js中拿出来的一个对象,也可以说json是js的一个子集。也就是说json的格式来源于JS的格式。

2.json使用的规则

1.js中支持单引号,也支持双引号,可以没有引号

 //在js中吧{}这样的类型叫做自定义对象,js中没有字典一说
data = {
    'name':'xiongda',
    "name":"xiongda",
     name:"xiongda"        
}     //js对象默认会把自己的键当成字符串处理,所以可以加引号也可以不加

2.json中支持的格式

  • json只认识双引号

  • json一定是一个字符串(字符串里面只能包含一个对象)

'{"name":"xiongda"}'

3.json的使用

// 合格的json对象
["one", "two", "three"]
{ "one": 1, "two": 2, "three": 3 }
{"names": ["张三", "李四"] }
[ { "name": "张三"}, {"name": "李四"} ]
​
// 不合格的json对象
{ name: "张三", 'age': 32 }  // 属性名必须使用双引号
[32, 64, 128, 0xFFF]  // 不能使用十六进制值
{ "name": "张三", "age": undefined }  // 不能使用undefined
{ "name": "张三",
  "birthday": new Date('Fri, 26 Aug 2011 07:13:10 GMT'),
  "getName":  function() {return this.name;}  // 不能使用函数和日期对象
}

3.python和js中的序列化/反序列化

python中的序列化/反序列化

python中序列化和反序列化需要导入模块json

  • json.dumps(obj):将python对象序列化为json格式字符串

  • json.loads(json对象):将json对象反序列化为python对象

import json
# s = "{'name':'chao','age':18}" #普通字符串,每加引号的没问题,加了引号的,必须是双引号才能使用json.loads()。
s = '{"name":"xiongda","age":18}'   #json字符串,里面必须是双引号
ret = json.loads(s)
print(ret,type(ret))  # {'name': 'xiongda', 'age': 18} <class 'dict'>
​
d = {'name': 'xiongda', 'age': 18}
ret = json.dumps(d)
print(ret,type(ret))  # {"name": "xiongda", "age": 18} <class 'str'>

js中的序列化和反序列化

js中的序列化和反序列化是通过JSON来实现,可以直接使用

  • JSON.stringify():将一个javascript对象转化为json字符串

  • JSON.parse():将一个json字符串转换为javascript对象

<script>
// js中的json的序列化
s2={'name':'xiongda'};
console.log(JSON.stringify(s2),typeof JSON.stringify(s2)) //string
// js中的json反序列化
s = '{"name":1}';    
var data = JSON.parse(s);
console.log(data);
console.log(typeof data);   //object
</script>

4.json和xml的比较

  1. XML也是存数据的一种格式,也是一种标记语言。它是利用节点进行查找的

  2. JSON 格式于2001年由 Douglas Crockford 提出,目的就是取代繁琐笨重的 XML 格式。

SON 格式有两个显著的优点:

  • 书写简单,一目了然;

  • 符合 JavaScript 原生语法,可以由解释引擎直接处理,不用另外添加解析代码。

所以,JSON迅速被接受,已经成为各大网站交换数据的标准格式,并被写入ECMAScript 5,成为标准的一部分。

json和xml使用实例

xml表示省市数据

<?xml version="1.0" encoding="utf-8"?>
<country>
    <name>中国</name>
    <province>
        <name>黑龙江</name>
        <cities>
            <city>哈尔滨</city>
            <city>大庆</city>
        </cities>
    </province>
    <province>
        <name>广东</name>
        <cities>
            <city>广州</city>
            <city>深圳</city>
            <city>珠海</city>
        </cities>
    </province>
    <province>
        <name>台湾</name>
        <cities>
            <city>台北</city>
            <city>高雄</city>
        </cities>
    </province>
    <province>
        <name>新疆</name>
        <cities>
            <city>乌鲁木齐</city>
        </cities>
    </province>
</country>
View Code

json表示省市数据

{
    "name": "中国",
    "province": [{
        "name": "黑龙江",
        "cities": {
            "city": ["哈尔滨", "大庆"]
        }
    }, {
        "name": "广东",
        "cities": {
            "city": ["广州", "深圳", "珠海"]
        }
    }, {
        "name": "台湾",
        "cities": {
            "city": ["台北", "高雄"]
        }
    }, {
        "name": "新疆",
        "cities": {
            "city": ["乌鲁木齐"]
        }
    }]
}
View Code

可以看到,JSON 简单的语法格式和清晰的层次结构明显要比 XML 容易阅读,并且在数据交换方面,由于 JSON 所使用的字符要比 XML 少得多,可以大大得节约传输数据所占用得带宽。

json的优缺点

JSON格式取代了xml给网络传输带来了很大的便利,但是却没有了xml的一目了然,尤其是json数据很长的时候,我们会陷入繁琐复杂的数据节点查找中。

但是国人的一款在线工具 BeJson 、SoJson在线工具让众多程序员、新接触JSON格式的程序员更快的了解JSON的结构,更快的精确定位JSON格式错误。

二、Ajax简介

1.什么是Ajax

Ajax(Asynchronous Javascript And XML)翻译成中文就是“异步的Javascript和XML”。

即使用Javascript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML,现在更多使用json数据)。

Ajax不是编程语言,而是一种使用现有标准的新方法,也就是前端向后端提交数据的方式。

使用Ajax的优点:

  • 无需重新加载页面,异步发送请求获取服务数据。

  • 局部刷新页面,增强用户体验

  • 浏览器无需响应全部页面,只是页面中少数内容,所以效率高。

Ajax不需要浏览器插件,但是需要浏览器中有JavaScript环境;

  • 同步交互:客户端发出一个请求后,需要等待服务器响应结束后,才能发出第二个请求;

  • 异步交互:客户端发出一个请求后,无需等待服务器响应结束,就可以发出第二个请求。

2.Ajax简单使用

我们以前知道的前端向后端发送数据的方式有:

  • GET:地址栏、a标签、Form表单

  • POST:Form表单

现在我们再学习一种:那就是ajax,也是前端向后端发送数据的一种方式

通过Ajax提交数据请求服务器,获取响应结果,根据响应结果局部更新当前页面,提示用户输入。

login.html页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
​
{% csrf_token %}
用户名:<input type="text" name="username" id="username"><br>
密码:<input type="password" name="password" id="password"><br>
<input type="button" id="sub" value="提交"><br>
<span class="info"></span><script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
<script>
    // 给sub标签绑定事件,提交数据
    $('#sub').click(function(){
        $.ajax({
            url:"{% url 'auth' %}",  // ajax异步请求的链接
            type:'post',  // ajax请求页面的方法
            data:{
                username:$('#username').val(),
                password:$('#password').val(),
           csrfmiddlewaretoken:$('input[name="csrfmiddlewaretoken"]').val()
            },  // 注意ajax提交数据,需要将数据封装在data中,和form提交数据不同,需要我们手动获取csrf_token的值,然后封装到data中,发送给服务端
            success:function (response) {
                console.log(response);  // response是请求成功后服务端返回的数据
                if (response==="1"){
                    alert("帐号或密码错误!")
                }
                else{
                    $('.info').val('帐号密码正确')
                }
            }
        })
    })
</script>
</body>
</html>
login.html

urls.py文件

from app01 import views
​
urlpatterns = [
    # url(r'^admin/', admin.site.urls),
    url(r'^login/', views.login,name='login'),
]
View Code

views.py文件

def auth(request):
    if request.method=="POST":
        username = 'alex'
        password = 'alex'
        if username == request.POST.get("username") and password == request.POST.get("password"):
            return HttpResponse(‘2’)
        else:
            return HttpResponse('1')
View Code

3.Ajax应用场景

使用场景

1.搜索引擎根据用户输入的关键字,不用刷新页面,自动提示检索关键字。

2.注册用户时对于用户名的查重,使用了Ajax

我们输入用户名后,并没有主动发送数据请求页面,但是网页上会显示该用户名已经被占用,重新选择用户名。

  • 整个过程页面没有刷新,只有局部信息刷新

  • 检查用户名请求发送后,我们无需等待,可以操作浏览器

其实本质上,就是给input标签绑定了一个失焦事件,当标签失焦后,会通过ajax发送请求,判断用户名是否存在,根据返回信息,局部刷新页面,从而提示用户。

ajax的特点(优点)

  • AJAX使用Javascript技术向服务器发送异步请求;

  • AJAX无须刷新整个页面;

  • 因为服务器响应内容不再是整个页面,而是页面中的局部,所以AJAX性能高;

三、Ajax的使用

1.基于jQuery实现

{% load staticfiles %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="{% static 'js/jquery-3.1.1.js' %}"></script>
</head>
<body><button class="send_Ajax">send_Ajax</button>
<script>
    // $.ajax的两种使用方式:
        // $.ajax(settings);
        // $.ajax(url,[settings]);
​
    $(".send_Ajax").click(function () {
​
        $.ajax({
            url: "/handle_Ajax/",
            type: "POST",
            data: {username: "Yuan", password: 123},
            //==================成功的时候要执行的代码==================
            success: function (response) {
                alert(response)
            },
​
            //=================== error的时候要执行的代码============
            error: function (jqXHR, textStatus, err) {
​
                // jqXHR: jQuery增强的xhr
                // textStatus: 请求完成状态
                // err: 底层通过throw抛出的异常对象,值与错误类型有关
                console.log(arguments);
            },
​
            //============ complete(无论成功还是失败,都要执行的代码)============
            complete: function (jqXHR, textStatus) {
                // jqXHR: jQuery增强的xhr
                // textStatus: 请求完成状态 success | error
                console.log('statusCode: %d, statusText: %s', jqXHR.status, jqXHR.statusText);
                console.log('textStatus: %s', textStatus);
            },
​
            //============= statusCode============
            statusCode: {
                '403': function (jqXHR, textStatus, err) {
                    console.log(arguments);  //注意:后端模拟errror方式:HttpResponse.status_code=500
                },
​
                '400': function () {
                }
            }
        })
    })
</script>
</body>
</html>
基于jQuery实现ajax请求

2.基于原生js实现(了解)

var b2 = document.getElementById("b2");
  b2.onclick = function () {
    // 原生JS
    var xmlHttp = new XMLHttpRequest();
    xmlHttp.open("POST", "/ajax_test/", true);
    xmlHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    xmlHttp.send("username=chao&password=123456");
    xmlHttp.onreadystatechange = function () {
      if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
        alert(xmlHttp.responseText);
      }
    };
  };
基于原生js实现ajax请求

四、Ajax参数详解

1.请求参数

data参数

data: 当前ajax请求要携带的数据,是一个json的object对象,ajax方法就会默认地把它编码成某种格式(urlencoded:?a=1&b=2)发送给服务端;此外,ajax默认以get方式发送请求。

function testData() {
    $.ajax("/test", {     //此时的data是一个json形式的对象
        data: {
            a: 1,
            b: 2
        }
    });                   //?a=1&b=2
}

processData参数

processData:声明当前的data数据是否进行转码或预处理,默认为true,即预处理;

if为false,那么对data:{a:1,b:2}会调用json对象的toString()方法,即{a:1,b:2}.toString() ,最后得到一个[object,Object]形式的结果。

contentType参数

contentType:默认值: "application/x-www-form-urlencoded"。发送信息至服务器时内容编码类型。

用来指明当前请求的数据编码格式;类似urlencoded:?a=1&b=2的格式发送数据。

contentType的三种类型:

  • application/x-www-form-urlencoded;默认值,类似urlencoded:?a=1&b=2的格式发送数据

  • multipart/form-data;提交文件的格式。

  • application/json;以json格式发送数据。

详细使用见下方

traditional参数

traditional:一般是我们的data数据有数组时会用到 :data:{a:22,b:33,c:["x","y"]},traditional为false会对数据进行深层次迭代;

2.响应参数

dataType参数

预期服务器返回的数据类型,服务器端返回的数据会根据这个值解析后,传递给回调函数。

默认不需要显性指定这个属性,ajax会根据服务器返回的content Type来进行转换;

比如我们的服务器响应的content Type为json格式,这时ajax方法就会对响应的内容 进行一个json格式的转换。

如果转换成功,我们在success的回调函数里就会得到一个json格式的对象;转换失败就会触发error这个回调函数。如果我们明确地指定目标类型,就可以使用data Type。dataType的可用值:html|xml|json|text|script

五、Ajax跨站请求伪造csrf_token

1.手动封装键值对(推荐)

直接获取隐藏input标签的csrfmiddlewaretoken值,组成键值对放在data中发送到服务端

注意键名是csrfmiddlewaretoken固定的,不能变

$.ajax({
  url: "/cookie_ajax/",
  type: "POST",
  data: {
    "username": "ryxiong",
    "password": 123,
    "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val()  // 使用jQuery取出csrfmiddlewaretoken的值,拼接到data中发送到服务端
  },
    success: function (data) {
    console.log(data);
  }
})

2.通过ajax设置渲染,只能在html页面中使用

$.ajaxSetup({
    data: {csrfmiddlewaretoken: '{{ csrf_token }}'}
});

注意:要放在ajax请求的前面,在发送之前组装一组字符串,在第一步render的时候就发送了,所以有局限性:如果把JS代码放到静态文件中,并不会渲染,不会执行{{csrf_token}},只能在HTML页面中使用。

3.手动封装数据头

通过获取返回的cookie中的字符串,放在请求头中发送。

注意:需要引入jquery.cookie.js插件

<script src="https://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.js"></script>
$.ajax({
    headers:{"X-CSRFToken":$.cookie('csrftoken')}, // 其实在ajax里面还有一个参数是headers,自定制请求头,可以将csrf_token加在这里,我们发contenttype类型数据的时候,csrf_token就可以这样加
});

六、jQuery和DJango中序列化

1.jQuery.serializer()序列化

serialize()函数用于序列化一组表单元素,将表单内容编码为用于提交的字符串。

  • serialize()函数常用于将表单内容序列化,以便用于AJAX提交。

该函数主要根据用于提交有效表单控件的name和value,将它们拼接为一个可直接用于表单提交的文本字符串,该字符串已经过标准的URL编码处理(字符集编码为UTF-8)。

无效的表单控件不会被提交,包括:

  • 不在<form>标签内的表单控件不会被提交

  • 没有name属性的表单控件不会被提交

  • 带有disabled属性的表单控件不会被提交

  • 没有被选中的表单控件不会被提交

语法

jQueryObject.serialize()

serialize()函数的返回值为String类型,返回将表单元素编码后的可用于表单提交的文本字符串。

html初始代码

<form name="myForm" action="http://www.365mini.com" method="post">
    <input name="uid" type="hidden" value="1" />
    <input name="username" type="text" value="张三" />
    <input name="password" type="text" value="123456" />
    <select name="grade" id="grade">
        <option value="1">一年级</option>
        <option value="2">二年级</option>
        <option value="3" selected="selected">三年级</option>
        <option value="4">四年级</option>
        <option value="5">五年级</option>
        <option value="6">六年级</option>
    </select>
    <input name="sex" type="radio" checked="checked" value="1" /><input name="sex" type="radio" value="0" /><input name="hobby" type="checkbox" checked="checked" value="1" />游泳
    <input name="hobby" type="checkbox" checked="checked" value="2" />跑步
    <input name="hobby" type="checkbox" value="3" />羽毛球
    <input name="btn" id="btn" type="button" value="点击" />
</form>
  • 序列化form表单中的所有元素

alert( $("form").serialize());
// 序列化结果: uid=1&username=%E5%BC%A0%E4%B8%89&password=123456&grade=3&sex=1&hobby=1&hobby=2
  • 序列化部分表单元素

alert( $(":text, select, :checkbox").serialize());
// 序列化后的结果:username=%E5%BC%A0%E4%B8%89&password=123456&grade=3&hobby=1&hobby=2

使用实例

html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width">
    <title>Title</title>
</head>
<body>
<form name="myForm" action="http://www.365mini.com" method="post">
    <input name="uid" type="hidden" value="1" />
    <input name="username" type="text" value="张三" />
    <input name="password" type="text" value="123456" />
    <select name="grade" id="grade">
        <option value="1">一年级</option>
        <option value="2">二年级</option>
        <option value="3" selected="selected">三年级</option>
        <option value="4">四年级</option>
        <option value="5">五年级</option>
        <option value="6">六年级</option>
    </select>
    <input name="sex" type="radio" checked="checked" value="1" /><input name="sex" type="radio" value="0" /><input name="hobby" type="checkbox" checked="checked" value="1" />游泳
    <input name="hobby" type="checkbox" checked="checked" value="2" />跑步
    <input name="hobby" type="checkbox" value="3" />羽毛球
    <input name="btn" id="btn" type="button" value="点击" />
</form>
<script src="/static/jquery-3.2.1.min.js"></script>
<script src="https://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.js"></script>
<script>
    $("#btn").click(function () {
{#        方式一#}
        //$.ajaxSetup({
          // data:{csrfmiddlewaretoken:'{{ csrf_token }}'}
        //});
        $.ajax({
            url:"/serialize/",
            type:"POST",
{#            方式三#}
            headers:{"X-CSRFToken":$.cookie('csrftoken')},
            //data:$("form").serialize(),   //序列form表单所有的
            data:$(":text,:password,:checkbox").serialize(),  //序列自己选择的
            success:function (data) {
                var data=JSON.parse(data);  //js中的反序列化
                console.log(data);
                console.log(typeof data);
                $(".error").html(data);
            }
        })
    })
</script>
</body>
</html>
serialize.html

views.py

def serialize(request):
    # form = request.POST
    # print(form)
    name = request.POST.get("username")
    password = request.POST.get("password")
    checked = request.POST.getlist("hobby")
    print(name,password,checked)
    return HttpResponse(json.dumps(name))

当有好多input的时候,就得一一对应的吧所有的数据发过去的,这样显得麻烦,我们用序列化。

// 前端
data:$("form").serialize(),   //序列form表单所有的
data:$(":text,:password,:checkbox").serialize(),  //序列自己选择的

在服务端获取数据

form = request.POST   
print(form)   #获取所有
name = request.POST.get("username")
password = request.POST.get("password")
checked = request.POST.getlist("hobby")
print(name,password,checked)#获取单个

2.Django内置的serializers

django中内置的serializers可以models中查到的QuerySet对象序列化为json格式,返回给前端,前端通过JSON.parse()方法可以获取

def books_json(request):
    book_list = models.Book.objects.all()[0:2]
    from django.core import serializers
    ret = serializers.serialize("json", book_list)  # [{"model": "app01.book", "pk": 1, "fields": {"title": "jinpingmei", "price": 200}}, {"model": "app01.book", "pk": 2, "fields": {"title": "xiaoheishi", "price": 1}}]
    return HttpResponse(ret)

但是这样序列化后的数据并不清晰,也不太好取,所以一般推荐下面方式获取models数据

book_objs = models.Book.objects.all().values('title','price')
books = list(book_objs)
print(books)  # [{'title': 'jinpingmei', 'price': 200}, {'title': 'xiaoheishi', 'price': 1}]

关于json序列化中的注意点

原始json序列化是无法直接序列化python中的事件对象的(datetime, date),如果需要序列化时间对象,需要使用一个类;

import json
from datetime import datetime
from datetime import date
​
# 对含有日期格式数据的json数据进行转换
class JsonCustomEncoder(json.JSONEncoder):
    def default(self, field):
        if isinstance(field,datetime):
            return field.strftime('%Y-%m-%d %H:%M:%S')
        elif isinstance(field,date):
            return field.strftime('%Y-%m-%d')
        else:
            return json.JSONEncoder.default(self,field)
​
dt = datetime.now()
d = json.dumps(d1,cls=JsonCustomEncoder)  # 指定序列化的类
print(d)

这个类的本质就是判断了需要序列化的对象的类型,如果是datetime或者date类型,将他们字符串格式化成了字符串时间,这样json就能够正常序列化。

七、Ajax文件上传

1.请求头ContentType详解

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

application/x-www-form-urlencoded

最常见的 POST 提交数据的方式。

浏览器的原生 <form> 表单,如果不设置 enctype 属性,那么最终就会以默认格式application/x-www-form-urlencoded 方式提交数据,ajax默认也是这个。

application/x-www-form-urlencoded方式会将数据封装成类似如下格式发送;

POST http://www.example.com HTTP/1.1
Content-Type: application/x-www-form-urlencoded;charset=utf-8
​
user=yuan&age=22  // 这就是上面这种contenttype规定的数据格式,后端对应这个格式来解析获取数据,不管是get方法还是post方法,都是这样拼接数据

multipart/form-data

这也是一个常见的 POST 数据提交的方式。我们使用表单上传文件时,必须让 <form> 表单的 enctype 属性等于 multipart/form-data。

form表单不支持发json类型的contenttype格式的数据,而ajax什么格式都可以发,也是ajax应用广泛的一个原因。

注意:multipart/form-data上传数据是分段的,一段64kb。

<h1>form上传文件</h1>
<form action="{% url 'upload' %}" method="post" enctype="multipart/form-data">  <!--上传文件必须指定enctype属性为multipart/form-data-->
    {% csrf_token %}  <!--csrf认证机制-->
    用户名:<input type="text" name="username">
    密码:<input type="password" name="password">
    头像:<input type="file" name="portrait">
    <input type="submit">
</form>

application/json

application/json的数据提交方式,是告诉服务器消息主体是序列化后的json字符串,json规范的广泛流行,使用json发送数据基本不会出现问题。

<h1>ajax上传文件</h1>
{% csrf_token %}
用户名:<input type="text" name="username">
密码:<input type="password" name="password">
<input type="button" id="upload" value="提交"><script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
<script>
    $("#upload").click(function(){
        var username = $("[name=username]").val();
        var password = $("[name=password]").val();
        $.ajax({
            url:"{% url 'upload' %}",  // 反向解析请求路径
            type:'post',
            contentType:"json",  // 指定发送数据为json格式
            data:JSON.stringify({  // 序列化数据为json格式
                username:username,
                password:password,
                csrfmiddlewaretoken:$("[name=csfrmiddlewaretoken]"),
            }),
            success:function (response) {
                console.log(response)
            }
        })
​
    })
</script>

服务端视图函数

def upload(request):
    """
    处理contentTpye为json的请求数据
    :param request:
    :return:
    """
    if request.method=="GET":
        return render(request, "upload.html")
    else:
        print(request.GET)  # <QueryDict: {}>
        print(request.POST)  # <QueryDict: {}>
        print(request.body)  # b'{"username":"ryxiong","password":"ryxiong",...}
        return HttpResponse("ok")

注意:通过contentType=json提交数据后,数据不存在GET/POST中,而是在请求体的body中。

而且django没有帮我们自动解析json数据,需要我们自己去解析这些数据,

2.基于form表单的文件上传

模板部分upload.html

<h1>form上传文件</h1>
<form action="{% url 'upload' %}" method="post" enctype="multipart/form-data">  <!--上传文件必须指定类型-->
    {% csrf_token %}  <!--csrf认证机制-->
    用户名:<input type="text" name="username">
    密码:<input type="password" name="password">
    头像:<input type="file" name="portrait">
    <input type="submit">
</form>

视图部分views.py

def upload(request):
    """
    处理form上传文件的函数
    :param request:
    :return:
    """
    if request.method=="GET":
        return render(request, "upload.html")
    else:
        print(request.POST)  # post中没有文件的信息
        username = request.POST.get('username')
        password = request.POST.get('password')
        file_obj = request.FILES.get("portrait")  # 获取文件对象
        print(file_obj,type(file_obj))  # 一个文件句柄  吃冰淇淋的小女孩.jpg <class 'django.core.files.uploadedfile.TemporaryUploadedFile'> 
        filepath = os.path.join(settings.BASE_DIR,'statics','img',file_obj.name)
        with open(filepath,'wb') as f:
            for chunk in file_obj.chunks():
                # chunks()方法,每次取固定大小64kb
                f.write(chunk)
​
        return HttpResponse("ok")

3.基于ajax上传文件

FormData是什么呢?

XMLHttpRequest Level 2添加了一个新的接口FormData.利用FormData对象,我们可以通过JavaScript用一些键值对来模拟一系列表单控件,我们还可以使用XMLHttpRequest的send()方法来异步的提交这个"表单"。

比起普通的ajax,使用FormData的最大优点就是我们可以异步上传一个二进制文件.

所有主流浏览器的较新版本都已经支持这个对象了,比如Chrome 7+、Firefox 4+、IE 10+、Opera 12+、Safari 5+。

注意:通过input文件标签id=upload_file拿到的对象如下

$("#upload_file")  // 拿到的是一个集合
$("#upload_file")[0]   // 就是一个dom对象
$("#upload_file")[0].files   // 拿到的是一个filelist
$("#upload_file")[0].files[0]  // 拿到的是当前最近的文件对象 

注意:要是使用FormData一定要加上

  • contentType:false // 不指定内容格式,按原始数据发送

  • processDate:false // 数据不做预处理

如果不加上,在前端会报错,Illegal innovation的异常。

使用实例

使用ajax上传文件,就不需要通过form表单来提交数据了,之前form表单支持的数据提交格式没有json,ajax就可以以任何数据格式提交。

1.模板文件upload.html,手动组合csrf键值对发送

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body><h1>ajax上传文件</h1>
{% csrf_token %}
用户名:<input type="text" name="username">
密码:<input type="password" name="password">
头像:<input type="file" name="file_obj">
<input type="button" id="upload" value="提交">
​
​
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script><script>
    $("#upload").click(function(){
        var formdata = new FormData();  // ajax传送文件需要通过FormData的实例来携带传送,
        var username = $("[name=username]").val();
        var password = $("[name=password]").val();
        var file_obj = $("[name=file_obj]")[0].files[0];  // 注意先转换成DOM对象,才能调用files方法,获得是一个文件的列表,通过索引取值
        var csrf_data = $('[name=csrfmiddlewaretoken]').val();
​
        // formdata对象携带数据方法:append
        formdata.append('username',username);
        formdata.append('password',password);
        formdata.append('file_obj',file_obj);
​
        // =========手动封装csrf键值对发送============
        formdata.append('csrfmiddlewaretoken',csrf_data);
​
        $.ajax({
            url:"{% url 'upload' %}",
            type:'post',
            data:formdata,
            processData:false,  // 不处理数据,按原始数据发送
            contentType:false,  // 不设置内容类型,就是不进行编码
            success:function (response) {
                console.log(response)
            }
        });
    })
</script>
</body>
</html>
手动添加csrf键值对

2.模板文件另一种模式,请求头中封装csrf_token

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body><h1>ajax上传文件</h1>
{% csrf_token %}
用户名:<input type="text" name="username">
密码:<input type="password" name="password">
头像:<input type="file" name="file_obj">
<input type="button" id="upload" value="提交">
​
​
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
<script src="https://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.js"></script>  <!--引入jquery中的cookie.js文件--><script>
    $("#upload").click(function(){
        var formdata = new FormData();  // ajax传送文件需要通过FormData的实例来携带传送,
        var username = $("[name=username]").val();
        var password = $("[name=password]").val();
        var file_obj = $("[name=file_obj]")[0].files[0];  // 注意先转换成DOM对象,才能调用files方法,获得是一个文件的列表,通过索引取值
// formdata对象携带数据方法:append
        formdata.append('username',username);
        formdata.append('password',password);
        formdata.append('file_obj',file_obj);
​
        // =========设置请求头csrf认证============
        $.ajax({
            url:"{% url 'upload' %}",
            type:'post',
            data:formdata,
            processData:false,  // 不处理数据,按原始数据发送
            contentType:false,  // 不设置内容类型,就是不进行编码
            headers:{"X-CSRFToken":$.cookie('csrftoken')},  // 头部附带cookie的csrf数据发送
            success:function (response) {
                console.log(response)
            }
        });
    })
</script>
</body>
</html>
头部封装cookie中csrf数据

视图文件views.py

def upload(request):
    """
    处理ajax上传文件的函数
    :param request:
    :return:
    """
    if request.method=="GET":
        return render(request, "upload.html")
    else:
        file_obj = request.FILES.get("file_obj")  # 获取文件对象,键名要与发送的数据键名一直
        print(file_obj,type(file_obj))  # 一个文件句柄  吃冰淇淋的小女孩.jpg <class 'django.core.files.uploadedfile.TemporaryUploadedFile'>
        filepath = os.path.join(settings.BASE_DIR,'statics','img',file_obj.name)
        with open(filepath,'wb') as f:
            for chunk in file_obj.chunks():
                f.write(chunk)
​
        return HttpResponse("ok")
upload视图函数

 

 

posted @ 2019-05-30 22:14  ryxiong728  阅读(851)  评论(0编辑  收藏  举报