Ajax介绍及重要应用场景

向服务器发送请求的途径

1、浏览器的地址栏:GET请求

2、form表单:GET与POST均可

3、a标签:GET请求

4、Ajax:用的最多、最广

5、其余的还有:link标签的href属性、img标签的src属性、script标签的src属性

Ajax简介

AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步的Javascript和XML”。即使用Javascript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML,现在更多使用json数据)。

AJAX 不是新的编程语言,而是一种使用现有标准的新方法。

AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。(这一特点给用户的感受是在不知不觉中完成请求和响应过程)

AJAX 不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。

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

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

AJAX除了异步的特点外,还有一个就是:浏览器页面局部刷新;(这一特点给用户的感受是在不知不觉中完成请求和响应过程。

对于AJAX的操作,其实就是两点:一是向后台提交请求数据;二是回调函数接收到后台响应的数据后进行的处理。

简单事例

利用ajax实现局部计算~

urlpatterns = [
    ...
    url(r'^ajax_add/', views.ajax_add),
    url(r'^ajax_demo1/', views.ajax_demo1),
    ...   
]
路由
<!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, initial-scale=1">
  <title>AJAX局部刷新实例</title>
</head>
<body>

<input type="text" id="i1">+
<input type="text" id="i2">=
<input type="text" id="i3">
<input type="button" value="AJAX提交" id="b1">

<script src="/static/jquery-3.2.1.min.js"></script>
<script>
  $("#b1").on("click", function () {
    $.ajax({
      url:"/ajax_add/", //别忘了加双引号
      type:"GET",  
      data:{"i1":$("#i1").val(),"i2":$("#i2").val()}, //object类型,键值形式的,可以不给键加引号
      success:function (data) {
        $("#i3").val(data);
      }
    })
  })
</script>
</body>
</html>
html文件
def ajax_demo1(request):
    return render(request, "ajax_demo1.html")


def ajax_add(request):
    #time.sleep(10)  #不影响页面发送其他的请求
    i1 = int(request.GET.get("i1"))
    i2 = int(request.GET.get("i2"))
    ret = i1 + i2
    return JsonResponse(ret, safe=False)
    #return render(request,'index.html')  #返回一个页面没有意义,就是一堆的字符串,拿到了这个页面,你怎么处理,你要做什么事情,根本就没有意义
视图函数

Ajax常见应用场景

(1)搜索引擎根据用户输入的关键字,自动提示检索关键字。

(2)还有一个很重要的应用场景就是注册时候的用户名的查重:

其实这里就使用了AJAX技术!当文件框发生了输入变化时,使用AJAX技术向服务器发送一个请求,然后服务器会把查询到的结果响应给浏览器,最后再把后端返回的结果展示出来

  a.整个过程中页面没有刷新,只是刷新页面中的局部位置而已!

  b.当请求发出后,浏览器还可以进行其他操作,无需等待服务器的响应!

  当输入用户名后,把光标移动到其他表单项上时,浏览器会使用AJAX技术向服务器发出请求,服务器会查询名为lemontree7777777的用户是否存在,最终服务器返回true表示名为lemontree7777777的用户已经存在了,浏览器在得到结果后显示用户名已被注册!

  a.整个过程中页面没有刷新,只是局部刷新了;

  b.在请求发出后,浏览器不用等待服务器响应结果就可以进行其他操作;

Ajax的优缺点

Ajax 开发时,网络延迟——即用户发出请求到服务器发出响应之间的间隔——需要慎重考虑。不给予用户明确的回应,没有恰当的预读数据,或者对 XMLHttpRequest的不恰当处理,都会使用户感到延迟,这是用户不希望看到的,也是他们无法理解的。通常的解决方案是,使用一个可视化的组件来 告诉用户系统正在进行后台操作并且正在读取数据和内容。

Ajax适用场景

<1>.表单驱动的交互
<2>.深层次的树的导航
<3>.快速的用户与用户间的交流响应
<4>.类似投票、yes/no等无关痛痒的场景
<5>.对数据进行过滤和操纵相关数据的场景
<6>.普通的文本输入提示和自动完成的场景

。。。

Ajax不适用场景

<1>.部分简单的表单
<2>.搜索
<3>.基本的导航
<4>.替换大量的文本
<5>.对呈现的操纵

。。。

基于Ajax实现一个简单的登陆认证页面

登陆失败不刷新页面,提示用户登陆失败,登陆成功自动跳转到网站首页。

准备工作:创建用户信息

1、在models文件中新加一个UserInfo类:

class UserInfo(models.Model):
    username = models.CharField(max_length=12)
    password = models.CharField(max_length=20)

2、创建数据库的表

python3 manage.py makemigrations
python3 manage.py migrate

3、新建一条添加用户信息的路由:

url(r'^insert_user/',views.insert_user),

4、视图函数中添加用户登陆信息:

def insert_user(request):
    UserInfo.objects.create(username='whw',password='123')
    UserInfo.objects.create(username='ww',password='123')

    return HttpResponse('OK')

 实现流程

1、需要两个路由:登陆的界面及登陆成功后展示的界面index

url(r'^login/',views.login),
url(r'^index/',views.index),

2、视图函数内容如下:

注意:JsonResponse给ajax的回调函数返回的结果是一个字典(自定义对象)!用它的话就不用执行序列化与反序列化操作了!

from django.shortcuts import render
from django.http import JsonResponse

from book.models import *


def login(request):
    if request.method == 'GET':
        return render(request,'login.html')
    if request.method == 'POST':

        username = request.POST.get('uname')
        password = request.POST.get('pwd')
        dic = {}
        ret = UserInfo.objects.filter(username=username,password=password)
        if ret:
            dic={'num':123}
        else:
            dic={'num':456}
        return JsonResponse(dic)

def index(request):
    return render(request,'index.html')

3、login.html文件:

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Login</title>
    <link rel="stylesheet" href="{% static 'bootstrap-3.3.7/css/bootstrap.css' %}">
    <link rel="icon" href="{% static 'whw.ico' %}">
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-6 col-md-offset-3" style="margin-top: 66px;">
            <h2 class="text-center" style="color: red">登陆界面</h2>
            <br>

                {#记得加csrf_token#}
                {% csrf_token %}
                <div class="form-group">
                    <label for="user">用户名</label>
                    <input type="text" id="user" name="username" class="form-control" placeholder="用户名">
                    <span class="help-block"></span>
                </div>
                <div class="form-group">
                    <label for="pwd">密码</label>
                    <input type="password" id="pwd" name="password" class="form-control" placeholder="密码">
                    <span class="help-block"></span>
                </div>
                <input id="login_btn" type="button" class="btn btn-success btn-block" value="登 陆">
            <span id="prom" style="color:red"></span>

        </div>
    </div>
</div>


<script src="{% static 'jquery-3.4.1.js' %}"></script>
<script src="{% static 'bootstrap-3.3.7/js/bootstrap.js' %}"></script>
<script>
    $('#login_btn').click(function () {
        {# csrf认证用到 #}
        token = $('input[name=csrfmiddlewaretoken]').val();
        username = $('#user').val();
        password = $('#pwd').val();
        $.ajax({
            url:'/login/',
            type:'post',
            data:{
                {# csrf认证数据传给后台 #}
                csrfmiddlewaretoken:token,
                uname:username,
                pwd:password,
            },
            success:function (data) {
                console.log(data); {# JsonResponse返回的自定义对象(字典) #}
                console.log(data.num); {# num对应的数字 #}
                if(data.num === 123){
                    location.href='/index/';
                }else{
                    $('#prom').text('用户名或密码错误')
                }
            }
        })
    })
</script>
</body>
</html>
login.html

4、index页面简单:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Index</title>
</head>
<body>
    <h1 style="color:red;">登陆成功</h1>
</body>
</html>
index.html

用户注册时异步提示用户名已注册 **

注册这里我做了两个ajax异步效果:一是结合input标签的变化事件提示用户他写的用户名是否已经注册;二是对注册信息填写有误的情况进行提示。

新建路由

# 注册
re_path(r'^register/$',views.register,name='register'),
# 验证input框里的用户名是否已经注册
re_path(r'^cfmuser/$',views.cfmuser),

模板文件

模板文件我做了继承,base文件的内容如下:

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %} {% endblock title %}</title>
    <link rel="icon" href="{% static 'whw.ico' %}">
    <link rel="stylesheet" href="{% static 'bootstrap-3.3.7/css/bootstrap.css' %}">
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-6 col-md-offset-3" style="margin-top: 66px;" >
            <h2 class="text-center" style="color: red">火之国图书管理系统</h2>
            <br>
            {% block main %}

            {% endblock main %}
        </div>
    </div>
</div>


<script src="{% static 'jquery-3.4.1.js' %}"></script>
<script src="{% static 'bootstrap-3.3.7/js/bootstrap.js' %}"></script>

    {% block script %}


    {% endblock script %}


</body>
</html>
base.html

register.html文件内容如下:

{% extends 'base2.html' %}

{% block title %}
    注册
{% endblock title %}



{% block main %}
    <div>
        {#记得加csrf_token#}
        {% csrf_token %}
        <div class="form-group">
            {#            <label for="user">用户名</label>#}
            <input type="text" id="user" name="username" class="form-control" placeholder="用户名">
            <span style="color:red;" class="help-block" id="prompt"></span>
        </div>
        <div class="form-group">
            {#            <label for="r_user">确认用户名</label>#}
            <input type="text" id="r_user" name="password" class="form-control" placeholder="确认用户名">
            <span class="help-block"></span>
        </div>
        <div class="form-group">
            <input type="password" id="pwd" name="username" class="form-control" placeholder="密码">
            <span class="help-block"></span>
        </div>
        <div class="form-group">
            <input type="password" id="r_pwd" name="username" class="form-control" placeholder="确认密码">
            <span class="help-block"></span>
        </div>

        <input type="button" class="btn btn-success btn-block" id="register_btn" value="立即注册">

        <span style="color: red;" class="er"></span>
    </div>
    
{% endblock main %}



{% block script %}
    <script>

        //input标签的变化事件——局部刷新提示用户已经有这个用户名了
        $('#user').on("input propertychange", function () {
            var user = $('#user').val();
            var token = $('input[name="csrfmiddlewaretoken"]').val();
            //console.log(user);
            $.ajax({
                url: '/cfmuser/',
                type: 'post',
                data: {
                    csrfmiddlewaretoken: token,
                    username: user
                },
                success: function (data) {
                    //console.log(data);
                    if (data === 'YES') {
                        $('#prompt').text('抱歉,这个用户名已经被注册过了');
                    } else {
                        $('#prompt').text('');
                    }
                }
            })
        });

        //注册按钮的点击事件
        $('#register_btn').click(function () {
            ///alert(123);
            var token = $('input[name="csrfmiddlewaretoken"]').val();
            var user = $('#user').val();
            var r_user = $('#r_user').val();
            var pwd = $('#pwd').val();
            var r_pwd = $('#r_pwd').val();
            //进行简单的校验
            if (user === '' || pwd === '') {
                $('.er').text('用户名或者密码不能为空');
            } else if (user !== r_user || pwd !== r_pwd) {
                $('.er').text('两次输入的用户名或密码不一致');
            } else {
                $('.er').text('');
                //前面校验通过后进行ajax的操作
                //将数据传给后台
                $.ajax({
                    url: '{% url "register" %}',
                    type: 'post',
                    data: {
                        csrfmiddlewaretoken: token,
                        username: user,
                        password: pwd,
                    },
                    success: function (data) {
                        console.log(data);
                        if(data==='no'){
                             $('.er').text('这个用户名已经注册过了!');
                        }else{
                            alert('注册成功!前往登陆页面');
                            location.href = '{% url "book_login" %}';
                        }
                    }
                })
            }

        })


    </script>


{% endblock script %}
register.html

视图函数

def register(request):
    if request.method == 'GET':
        return render(request,'register.html')
    elif request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        ret = UserInfo.objects.filter(username=username,password=password)
        res = 'no'
        if ret:
            return HttpResponse(res)
        else:
            UserInfo.objects.create(username=username,password=password)
            res = 'yes'
            return HttpResponse(res)


def cfmuser(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        ret = UserInfo.objects.filter(username=username)
        res = 'NO'
        if ret:
            res = 'YES'
        return HttpResponse(res)

ContentType请求头 *****

请求头之ContentType

1、ContentType指的是请求体的编码类型,常见的类型共有3种:application/x-www-form-urlencoded、 multipart/form-data、application/json  

2、 application/x-www-form-urlencoded
这应该是最常见的 POST 提交数据的方式了。浏览器的原生表单,浏览器默认以这种方式编码。
如果不设置 enctype 属性,那么最终就会以 application/x-www-form-urlencoded 方式提交数据。

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

4、application/json
(1)application/json 这个 Content-Type 作为响应头大家肯定不陌生。
实际上,现在越来越多的人把它作为请求头,用来告诉服务端消息主体是序列化后的 JSON 字符串。 由于 JSON 规范的流行,除了低版本 IE 之外的各大浏览器都原生支持 JSON.stringify,服务端语言也都有处理 JSON 的函数,使用 JSON 不会遇上什么麻烦。 (
2)JSON 格式支持比键值对复杂得多的结构化数据,这一点也很有用。
之前遇到一个项目,需要提交的数据层次非常深,就是把数据 JSON 序列化之后来提交的。 不过当时我是把 JSON 字符串作为 val,仍然放在键值对里,以 x
-www-form-urlencoded 方式提交。
(3) 前后端分离项目、写接口、向别的程序请求数据~一定会用 application/json

#如果在ajax里面写上这个contenttype类型,那么data参数对应的数据,就不能是个object类型数据了,
#必须是json字符串,contenttype:'json',简写一个json,它也能识别是application/json类型:

#服务端接受到数据之后,通过contenttype类型的值来使用不同的方法解析数据,其实就是服务端框架已经写好了针对这几个类型的不同的解析数据的方法,
#通过contenttype值来找对应方法解析,如果有一天你写了一个contenttype类型,定义了一个消息格式,各大语言及框架都支持,
#那么别人也会写一个针对你的contenttype值来解析数据的方法,django里面不能帮我们解析contenttype值为json的数据格式,
#你知道他能帮你解析application/x-www-form-urlencoded 和multipart/form-data(文件上传会用到)就行了,
#如果我们传json类型的话,需要我们自己来写一个解析数据的方法,其实不管是什么类型,我们都可以通过原始发送来的数据来进行加工处理,解析出自己想要的数据,
#这个事情我们在写web框架的时候在获取路径那里用过

 

$.ajax({
        url:"{% url 'home' %}",
        type:'post',
        headers:{
            "X-CSRFToken":$.cookie('csrftoken'), //cookie进行csrf认证的方法
            contentType:'json',
        },

        //如果我们发送的是json数据格式的数据,那么csrf_token就不能直接写在data里面了,没有效果,
        //必须通过csrf的方式3的形式来写,写在hearders(请求头,可以写一些自定制的请求头)里面,
        //注意,其实contentType也是headers里面的一部分,写在里面外面都可以
        //上面进行了cookie的认证,下面就不用写csrf那个键值了
        data:JSON.stringify({ 
            name:name,
            //csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val(),
        }),
        success:function (response) {
        }
    })

form表单与Ajax上传单个文件 

见这篇博客:form表单与ajax单个文件的上传

form表单与Ajax上传多个文件 

见这篇博客:基于form表单与ajax上传多个文件

Ajax上传文件时进行大小限制 ******

创建路由

url(r'^file_limit/$',views.file_limit),

html文件

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form id="upload">
    {# csrftoken标签写在form表单里面 #}
    {% csrf_token %}
    <input type="file" name="my_file">
    <input id='btn' type="button" value="上传">
</form>

<script src="{% static 'jquery-3.4.1.js' %}"></script>
<script>
    $('#btn').click(function () {
        {# 注意里面的参数是一个dom对象! #}
        var formdata = new FormData($('#upload')[0]);

        $.ajax({
            url:'/file_limit/',
            type:'post',
            {# 将form表单的dom对象当作参数创建FormData对象后会自行进行csrftoken认证 #}
            data:formdata,
            processData:false,
            contentType:false,
            success:function (data) {
                console.log(data);
                if(data==='SIZE_NO'){
                    alert('文件大小不能超过1M!');
                }else{
                    alert('文件上传成功!');
                }
            }
        })
    })
</script>
</body>
</html>

视图函数

def file_limit(request):
    if request.method == 'POST':
        print(request.FILES)
        # 单个文件用get方法~
# 通过前端input file标签的的name属性获取文件对象
file_obj = request.FILES.get('my_file') # name方法获取文件名 filename = file_obj.name file_path = os.path.join(settings.BASE_DIR,'staticfiles','upload',filename) #size方法获取文件大小~进行判断 file_size = file_obj.size #限制文件大小不能超过1M if file_size > 1 * 1024 *1024: return HttpResponse('SIZE_NO') else: # 传文件 with open(file_path,'wb')as f: for data in file_obj.chunks(): f.write(data) return HttpResponse('OK') if request.method == 'GET': return render(request,'file_limit.html')

  多个文件上传时大小判断的思路:对于多个文件~上面这种方法的request.FILES.getlist(name属性)方法获取到的是文件对象的列表~可以在上传文件前遍历一下这个列表~查看每一个上传为文件大小是否符合要求~再进行后续的操作

django后端代码接受上传文件的方法

当Django处理上传一个文件的时候,文件数据被放在request.FILES中。这个文档解释文件怎么样被存储在磁盘上或者内存中,怎样定制默认的行为。
基本文件上传
考虑一个包含FileField的简单的表单:
from  django  import  forms
classUploadFileForm(forms.Form):
   title=forms.CharField(max_length=50)
   file=forms.FileField()
一个处理这个表单的视图将在request.FILES中接受文件数据 ,request.FILES是一个字典,它对每个FileField(或者是ImageField,或者是其他的FileField的子类)都包含一个key.所以 从上面的表单中来的数据将可以通过request.FILES['file']键来访问.
注意request.FILES只有 在request方法是POST并且发出POST请求的

有属性enctype="multipart/form-data".否则,request。FILES将是空的。
看另一个简单的;
from fdjango.http improt HttpResponseRedirect
from django.shortcuts import render_to_response
from somewhere import handle_uploaded_file
def upload_file(request):
    if request.method == 'post':
        form =  UploadFileForm(rquest.POST,request.FILES)
        if form.is_valid():
            handle_uploaded_file(request.FILES['file'])
            return HttpResponseRedirect('/success/ur/')
   else:
        form = UploadFileForm()
    return render_to_response('upload.html',{'form':form})
要注意,我们必须将request.FILES传递到表单的构造器中;这就是文件数据怎样和表单沾上边的 。
处理上传的文件
最后的难题是怎样处理从request.FILES中获得的真实的文件。这个字典的每个输入都是一个UploadedFile对象——一个上传之后的文件的简单的包装。
你通常会使用下面的几个方法来访问被上传的内容:
UploadedFile.read():从文件中读取整个上传的数据。小心整个方法:如果这个文件很大,你把它读到内存中会弄慢你的系统。你可以想要使用chunks()来代替,看下面;
UploadedFile.multiple_chunks():如果上传的文件足够大需要分块就返回真。默认的这个值是2.5兆,当然这个值是可以调节的,看下面的UploadedFile.chunks():一个产生器,返回文件的块。如果multiple_chunks()是真的话,你应该在一个循环中使用这个方法,而不是使用read();
UploadedFile.name:上传文件的名字(比如m_file.txt)
UploadedFile.size:以bytes表示的上传的文件的大小。
还有其他的几个方法和属性。你可以自己去查。
把他们放在一起,这里是一个你处理上传文件的通常方法:
def handle_uploaded_file(f):
    destination = open('some/file/name.txt','wb+')
    for chunk in f.chunks(): 
        destination.write(chunk)
    destination.close()
在UploadedFile.chunks()上循环而不是用read()保证大文件不会大量使用你的系统内存。
上传的数据存在哪里?
在你保存上传的文件之前,数据需要被保存在某些地方。默认呢的,如果一个上传的文件小于2.5兆,Django会将上传的东西放在内存里。这意味着只要从内存读取数据并保存到硬盘上,所以很快。然而,如果一个上传的文件太大,Django将将上传的文件写到一个临时的文件中,这个文件在你的临时文件路径中。在Unix-like的平台上意味着你可以预见Django产生一个文件保存为/tmp/tmpzfp6I6.upload的文件。如果这个文件足够大,你可以观察到这个文件的大小在增大。
很多细节--2.5M;/tmp;等 等 都是简单的看上去合理的默认值。继续阅读看看你怎么样个性化或者完全替代掉上传行为。
改变上传处理行为
三个设置改变Django的上传处理行为:
FILE_UPLOAD_MAX_MEMORY_SIZE:以bytes为单位的到内存中的最大大小,。比这个值大的文件将被先存到磁盘上。默认是2.5兆
FILE_UPLOAD_TEMP_DIR:比FILE_UPLOAD_MAX_MEMORY_SIZE大的文件将被临时保存的地方。默认是系统标准的临时路径。
FILE_UPLOAD_PERMISSIONS:如果这个没有给出或者是None,你将获得独立于系统的行为。大多数平台,临时文件有一个0600模式,从内存保存的文件将使用系统标准umask。
FILE_UPLOAD_HANDLERS:上传文件的处理器。改变这个设置允许完全个性化——甚至代替——Django的上传过程。
默认是:
("django.core.files.uploadhandler.MemoryFileUploadHandler",
 "django.core.files.uploadhandler.TemporaryFileUploadHandler",)
UploadedFile 对象
class UploadedFile
作为那些重File继承的补充,素有的UploadedFile对象定义了下面的方法和属性:
UploadedFile.content_type
文件的content_type头(比如text/plain
 orapplication/pdf
)。像用户提供的任何数据一样,你不应该信任上传的数据就是这个类型。你仍然要验证这个文件包含这个头声明的content-type——“信任但是验证”。
UploadedFile.charset
对于text/*的content-types,浏览器提供的字符集。再次,“信任但是验证”是最好的策略。
UploadedFile.temporary_file_path():只有被传到磁盘上的文件才有这个方法,它返回临时上传文件的全路径。
注意:
 像通常的Python文件,你可以迭代上传的文件来一行一行得读取文件:
for line in uploadedfile:
    do_something_with(line)
然而,不同于标准Python文件,UploadedFile值懂得/n(也被称为Unix风格)的结尾。如果你知道你需要处理有不同风格结尾的文件的时候,你要在你的视图中作出处理。
上传处理句柄:
当一个用户上传一个文件,Django敬爱那个这个文件数据传递给上传处理句柄——一个处理随着文件上传处理文件的小类。上传处理句柄被FILE_UPLOAD_HANDLERS初始化定义,默认是:
(
"django.core.files.uploadhandler.MemoryFileUploadHandler"
,
 "django.core.files.uploadhandler.TemporaryFileUploadHandler"
,)
这两个提供了Django处理小文件和大文件的默认上产行为。
你可以个性化处理句柄来个性化Django处理文件的行为。比如你可以使用个性化的处理句柄来强制用户配额,实时地压缩数据,渲染进度条,甚至在保存在本地的同时向另一个存储地发送数据。
实时修改上传处理句柄
有的时候某些视图要使用不同的上传行为。这种情况下,你可以重写一个上传处理句柄,通过request.upload_handlers来修改。默认的,这个列表包含FILE_UPLOAD_HANDLERS提供的处理句柄,但是你可以像修改其他列表一样修改这个列表。
比如,加入你写了一个叫做
ProgressBarUploadHandler
 的处理句柄。你可以通过下面的形式加到你的上传处理句柄中:
request.upload_handlers.insert(0,ProgressBarUploadHandler())
你赢使用list.insert()在这种情况下。因为进度条处理句柄需要首先执行。记住,处理句柄按照顺序执行。
如果你像完全代替掉上传处理句柄,你可以赋值一个新的列表:
request.upload_handlers=[ProgressBarUploadHandler()]
注意:你只能在访问request.POST或者request.FILES之前修改上传处理句柄。——如果上传处理开始后再改就没用了。如果你在修改reqeust.uplaod_handlers之前访问了request.POST
 or request.FILES
 ,Django将抛出一个错误。
所以,在你的视图中尽早的修改上传处理句柄。

写自定义的上传处理句柄:

所有的上传处理句柄都应 是 django.core.files.uploadhandler.FileUploadHandler的子类。你可以在任何你需要的地方定义句柄。
需要的方法:

自定义的上传处理句柄必须定义一下的方法:

FileUploadHandler.receive_data_chunk(self,raw_data,start):从文件上传中接收块。

raw_data是已经上传的字节流

start是raw_data块开始的位置

你返回的数据将被传递到下一个处理句柄的receive_data_chunk方法中。这样一个处理句柄就是另一个的过滤器了。

返回None将阻止后面的处理句柄获得这个块,当你 自己存储这个数据,而不想其他处理句柄存储拷贝时很有用。

如果你触发一个StopUpload或者SkipFile异常,上传将被放弃或者文件被完全跳过。

FileUploadHandler.file_complete(self, file_size)


当 文件上传完毕时调用。

处理句柄应该返回一个UploadFile对象,可以存储在request.FILES中。处理句柄也可以返回None来使得UploadFile对象应该来自后来的上传处理句柄。


剩下的就是可选的一些方法实现。


FILE_UPLOAD_MAX_MEMORY_SIZE = 209715200 
FILE_UPLOAD_MAX_MEMORY_SIZE = 209715200


在你本机先好好测试一下,它是如何占用内存,什么时候开始存入temp目录,怎么迁移到upload目录底下的

文件上传的时候,如果一个上传的文件小于2.5兆,Django会将上传的东西放在内存里,如果上传的文件大于2.5M,Django将整个上传的文件写到一个临时的文件中,这个文件在临时文件路径中。上传完毕后,将调用View中的_Upload()方法将临时文件夹中的临时文件分块写到上传文件的存放路径下,每块的大小为64K,写完后临时文件将被删除。


UploadedFile.multiple_chunks():如果上传的文件足够大需要分块就返回真。默认的这个值是2.5兆,当然这个值是可以调节的,看下面的UploadedFile.chunks():一个产生器,返回文件的块。如果multiple_chunks()是真的话,你应该在一个循环中使用这个方法,而不是使用read();

在你保存上传的文件之前,数据需要被保存在某些地方。默认呢的,如果一个上传的文件小于2.5兆,Django会将上传的东西放在内存里。这意味着只要从内存读取数据并保存到硬盘上,所以很快。然而,如果一个上传的文件太大,Django将上传的文件写到一个临时的文件中,这个文件在你的临时文件路径中。在Unix-like的平台上意味着你可以预见Django产生一个文件保存为/tmp/tmpzfp6I6.upload的文件。如果这个文件足够大,你可以观察到这个文件的大小在增大。

三个设置改变Django的上传处理行为:
FILE_UPLOAD_MAX_MEMORY_SIZE:以bytes为单位的到内存中的最大大小,。比这个值大的文件将被先存到磁盘上。默认是2.5兆
FILE_UPLOAD_TEMP_DIR:比FILE_UPLOAD_MAX_MEMORY_SIZE大的文件将被临时保存的地方。默认是系统标准的临时路径。
FILE_UPLOAD_PERMISSIONS:如果这个没有给出或者是None,你将获得独立于系统的行为。大多数平台,临时文件有一个0600模式,从内存保存的文件将使用系统标准umask。
Django上传文件的方法详解

Ajax请求设置csrf_token ******

方式一

通过获取隐藏的input标签中的csrfmiddlewaretoken值,放置在data中发送

这种方式比较常用~就是在我们的模板文件中先写一个标签~{% csrf_token %}~浏览器会自动给我们在页面中生成一个隐藏的input标签~将它的val值获取到后在ajax操作时放到data里传给后台就OK了!

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

方式二

利用jQuery.ajaxSetup()函数进行AJAX的全局默认设置。

该函数用于更改jQuery中AJAX请求的默认设置选项。之后执行的所有AJAX请求,如果对应的选项参数没有设置,将使用更改后的默认设置。

该函数属于全局jQuery对象。

具体介绍详见这篇博客:https://blog.csdn.net/pursuer211/article/details/44936827

主要用法:

就拿简单的用户名与密码提交来讲~

新建一条测试路由:

url(r'^test/$',views.test),

视图函数如下:

def test(request):
    if request.method == 'GET':
        return render(request,'test.html')
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        print(username,password)
        return HttpResponse('OK')
测试函数

主要看一下前端模板文件的实现:

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form >
    {# 注意form表单里没有加csrftoken标签 #}
    用户名:<input type="text" id="user">
    密码:<input type="password" id="pwd">
    <input type="button" id="btn" value="确认">
</form>
</body>
<script src="{% static 'jquery-3.4.1.js' %}"></script>
<script>
    $('#btn').click(function () {
        {# 更改jQuery中AJAX请求的默认设置选项 #}
        {# 这样设置也可以通过csrf认证 #}
        $.ajaxSetup({
            data:{
              csrfmiddlewaretoken:'{{ csrf_token }}'
            },
        });
        {# 设置ajax请求的数据  #}
        $.ajax({
            url:'/test/',
            type:'post',
            data:{
                username:$('#user').val(),
                password:$('#pwd').val(),
            },
            success:function (data) {
                console.log(data);
            }
        })

    })

</script>

</html>

在GET请求刷新页面的时候~我们可以看到已经生成了一个用于csrfmiddlewaretoken认证的随机字符串了~而且每次刷新页面后的值都是不一样的:

方式三

通过获取返回的cookie中的字符串 放置在请求头中发送。其实在ajax里面还有一个参数是headers,自定制请求头,可以将csrf_token加在这里,我们发contenttype类型数据的时候,csrf_token就可以这样加。

主要用在ajax上传文件的时候~~上面那个利用ajax上传文件为例子就用到了这种认证的方法!

注意:需要引入一个jquery.cookie.js插件。下载地址:http://plugins.jquery.com/cookie/
<script src="{% static 'jquery-3.4.1.js' %}"></script>
{# 注意引入jquery.cookie.js 进行csrfmiddlewaretoken认证 #}
<script src="{% static 'jquery-cookie/jquery.cookie.js' %}"></script>
......
 $.ajax({
            url:{% url 'ajax_upload'%},
            type:'post',
            data:formdata,
            // 从Cookie取csrftoken,并设置到请求头中,进行csrfmiddlewaretoken认证
            headers: {"X-CSRFToken": $.cookie('csrftoken')},

{# 用formdata进行ajax文件上传必须加这两个参数 #} processData:
false , // 不处理数据 contentType: false, // 不设置内容类型 success:function (data) { console.log(data); } }) ......

或者可以自己写一个getCookie方法:

function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}
var csrftoken = getCookie('csrftoken');
自定义getCookie方法

每一次都这么写太麻烦了,可以使用$.ajaxSetup()方法为ajax请求统一设置:

function csrfSafeMethod(method) {
  // these HTTP methods do not require CSRF protection
  return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}

$.ajaxSetup({
  beforeSend: function (xhr, settings) {
    if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
      xhr.setRequestHeader("X-CSRFToken", csrftoken);
    }
  }
});
jQuery全局设置

注意:

如果使用从cookie中取csrftoken的方式,需要确保cookie存在csrftoken值。

如果你的视图渲染的HTML文件中没有包含 {% csrf_token %},Django可能不会设置CSRFtoken的cookie。

这个时候需要使用ensure_csrf_cookie()装饰器强制设置Cookie。

from django.views.decorators.csrf import ensure_csrf_cookie

@ensure_csrf_cookie
def login(request):
    pass

 详细的见Django官方文档中CSRF内容:https://docs.djangoproject.com/en/1.11/ref/csrf/

      

 

posted on 2019-05-29 21:08  江湖乄夜雨  阅读(2145)  评论(0编辑  收藏  举报