Python3基础之学习笔记(十五)-Ajax-文件上传-图片验证码

1. Django

1.1 Ajax

1.1.1 Ajax简介

AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步Javascript和XML”。即使用Javascript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML,可以指定json),相对于Form表单而言的,提交时可以偷偷向后台发数据,页面不刷新;

2大特点:

  1. 和server端异步交互
  2. 偷偷向server端发送数据,页面

和form、a标签的区别:

ajax、form、a标签都可以基于HTML页面向server端发送request,

但本质区别是form、a标签一般向server索要的是页面,而ajax则是字符串数据。

Jquery和Ajax关系:

Jquery没有Ajax功能,它之所以可以调用Ajax向服务端提交数据,是因为Jquey封装了原生Ajax的代码

使用原生Ajax的优势:

使用Ajax直接使用JS的XMLHttp Request对象, 无需引入Jquery了。这样响应客户端携带信息量减少,可节省流量。

1.1.2 原生Ajax

Ajax主要就是使用 【XmlHttpRequest】对象来完成请求的操作,该对象在主流浏览器中均存在(除早起的IE),Ajax首次出现IE5.5中存在(ActiveX控件)。

XmlHttpRequest对象介绍

XmlHttpRequest对象的主要方法:

a. void open(String method,String url,Boolen async)
   用于创建请求
 
   参数:
       method: 请求方式(字符串类型),如:POSTGETDELETE...
       url:    要请求的地址(字符串类型)
       async:  是否异步(布尔类型)
 
b. void send(String body)
    用于发送请求
 
    参数:
        body: 要发送的数据(字符串类型)
 
c. void setRequestHeader(String header,String value)
    用于设置请求头
 
    参数:
        header: 请求头的key(字符串类型)
        vlaue:  请求头的value(字符串类型)
 
d. String getAllResponseHeaders()
    获取所有响应头
 
    返回值:
        响应头数据(字符串类型)
 
e. String getResponseHeader(String header)
    获取响应头中指定header的值
 
    参数:
        header: 响应头的key(字符串类型)
 
    返回值:
        响应头中指定的header对应的值
 
f. void abort()
 
    终止请求

XmlHttpRequest对象的主要属性:

a. Number readyState
   状态值(整数)
 
   详细:
      0-未初始化,尚未调用open()方法;
      1-启动,调用了open()方法,未调用send()方法;
      2-发送,已经调用了send()方法,未接收到响应;
      3-接收,已经接收到部分响应数据;
      4-完成,已经接收到全部响应数据;
 
b. Function onreadystatechange
   当readyState的值改变时自动触发执行其对应的函数(回调函数)
 
c. String responseText
   服务器返回的数据(字符串类型)
 
d. XmlDocument responseXML
   服务器返回的数据(Xml对象)
 
e. Number states
   状态码(整数),如:200404...
 
f. String statesText
   状态文本(字符串),如:OK、NotFound...

实例

views.py

def ajax(request):
    return render(request,'ajax.html')
def ajax_json(request):
    from django.http import JsonResponse
    if request.method=='GET':
        return JsonResponse({'name':'小张','age':18})
    if request.method=='POST':
        name=request.POST.get('name')
        age=request.POST.get('age')
        print(name)
        print(age)
        return JsonResponse({'name':name,'age':age})

ajax.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ajax</title>
</head>
<body>
<input type="button" value="原生ajax" onclick="ajaxl();">
<script>
    {#跨浏览器支持#}
    function getXHR() {
        var xhr=null;
        if(XMLHttpRequest){
            xhr=new XMLHttpRequest();
        }
        else {
            xhr=new ActiveXObject('Microsoft.XMLHTTP');
        }
        return xhr;
    }
    function ajaxl() {
        var xhr=getXHR();
        {#xhr.open('GET','/ajax_json',true);#}
        xhr.open('POST','/ajax_json',true);
        xhr.onreadystatechange=function () {
            if(xhr.readyState==4){
                //数据接收完毕
                var obj=JSON.parse(xhr.responseText);
                console.log(obj);
            }
        }
        {#POST请求必须设置这个请求头#}
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset-UTF-8');
        xhr.send('name=张三;age=35')
    }
</script>
</body>
</html>

1.1.3 jquery的ajax

function ajax_jq() {
        $.ajax(
            {
                url:'/ajax_json',
                type:'POST',
                {# traditional:一般是我们的data数据有数组时会用到 #}
                traditional:false,
                data:{'name':'张三','age':'35'},
                {#  ----------------- ajax的回调函数----------------------#}
                {#       1、server端response 200成功,执行的回调函数#}
                success:function (data) {
                        data=JSON.parse(data)
                        console.log(typeof data)},
                {#      2、server端 response错误,执行的回调函数 #}
                error:function () {
                        console.log(arguments)
                        alert(123) },
                {#      3、无论server端返回的结果如何,都会执行的回调函数#}
                complete:function () {
                        alert(321)  },
                {#      4、根据server端返回的状态码,执行的回调函数#}
                statusCode:{
                '403':function () {alert(403)},
                '503':function () {alert(503)}}
          })
    }

views.py

def ajax_json(request):
    from django.http import JsonResponse
    if request.method=='GET':
        return JsonResponse({'name':'小张','age':18})
    if request.method=='POST':
        name=request.POST.get('name')
        age=request.POST.get('age')
        print(name)
        print(age)
        JsonResponse.status_code=503
        return JsonResponse({'name':name,'age':age})

1.1.4 伪Ajax

所谓伪装Ajax操作就是不利于任何Ajax,利于其他技术向后台发送数据,这个其他的技术要从 iframa标签说起

<form action="/ajax_json" method="post" target="ifm">
        <iframe id='ifm' name="ifm" ></iframe>
        <input type="text" name="username">
        <input type="text" name="age">
        <input type="submit" onclick="sumbitForm();" value="form提交">
    </form>
<script>
function sumbitForm(){
        $('#ifm').load(function () {
            var con=$('#ifm').contents().find('body').text();
            console.log(con);
        });
    }
</script>

1.1.5 Ajax选择

上传方法总结

上传文件—>iframe+input(伪造ajax)

上传数据---->Jquery、XMLHttpRespose(原生ajax)

图片预览功能实现思路:

1、上传图片到服务端

2、服务端返回一个URL到客户端

3、客户端再去通过img标签src服务端的返回的URL地址

1.2 文件上传

原生ajax文件上传

{% extends 'base.html' %}
{% block title %}ajax{% endblock %}
{% block content %}
    <div class="container" style="margin-top: 30px;">
        <div class="col-xs-12 col-sm-4 col-md-4">
            <div class="file-container"
                 style="display:inline-block;position:relative;overflow: hidden;vertical-align:middle">
                <button class="btn btn-success fileinput-button" type="button">上传</button>
                <input type="file" id="jobData" onchange="loadFile(this.files[0])"
                       style="position:absolute;top:0;left:0;font-size:34px; opacity:0">
            </div>
            <span id="filename" style="vertical-align: middle">未上传文件</span>
            <input class="btn btn-outline-primary" type="button" value="提交" onclick="sumbitFile();"></input>
        </div>
 
    </div>
{% endblock %}
{% block custom_js %}
    <script >
        function loadFile(file) {
            $("#filename").html(file.name);
        }
        function sumbitFile() {
           var file_obj=document.getElementById('jobData').files[0];
           var fd=new FormData();
           fd.append('upload_file',file_obj);
           var xhr=new XMLHttpRequest();
           xhr.open('POST','/upload_file',true);
           xhr.onreadystatechange=function () {
               if(xhr.readyState==4){
                 var obj=JSON.parse(xhr.responseText);
                 console.log(obj);
               }
           };
            xhr.send(fd);
        }
    </script>
{% endblock %}

jQuery提交

function sumbitFileJquery() {
            var file_obj = $('#jobData')[0].files[0];
            var fd = new FormData();
            fd.append('upload_file', file_obj);
            $.ajax({
                url: '/upload_file',
                type: 'POST',
                contentType: false,
                {#告诉Jquery不要对请求头做特殊处理,因为formDta已经把数据封装好了#}
                processData: false,
                data: fd,
                success: function (args) {
                   console.log(args)
                }
            })
        }

iframe+form标签伪造ajax操作(兼容性更好)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>伪造Ajax上传</title>
</head>
<body>
<h1>伪造Ajax上传</h1>
<form method="post" id="f1" action="/upload_file" target='ifr'enctype="multipart/form-data">
  <p><input type="file" name="k1"></p>
  <p><iframe id="ifr"name="ifr" style="display: none"></iframe></p>
  <p><input type="button" value="提交" onclick="upfile()"></p>
</form>
<div id="container"></div>
</body>
<script>
function upfile() {
{#    找到iframe标签在线绑定事件 页面加载事件()#}
    document.getElementById('ifr').onload=loadIframe
{#    使用.submit()方法 提交表单#}
    document.getElementById('f1').submit()
                }
function loadIframe() {
{#    服务端有返回值 iframe标签就会加载,利用此特性绑定 加载事件,然后创建img标签,apendchilder到 一个div中显示 #}
    var return_value=document.getElementById('ifr').contentWindow.document.body.innerHTML
    var tag=document.createElement('img')
    tag.src='/'+return_value
    document.getElementById('container').appendChild(tag)}
</script>
</html>

1.3 图片验证码

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import random
from PIL import Image, ImageDraw, ImageFont, ImageFilter

_letter_cases = "abcdefghjkmnpqrstuvwxy"  # 小写字母,去除可能干扰的i,l,o,z
_upper_cases = _letter_cases.upper()  # 大写字母
_numbers = ''.join(map(str, range(3, 10)))  # 数字
init_chars = ''.join((_letter_cases, _upper_cases, _numbers))


def create_validate_code(size=(120, 30),
                         chars=init_chars,
                         img_type="GIF",
                         mode="RGB",
                         bg_color=(255, 255, 255),
                         fg_color=(0, 0, 255),
                         font_size=18,
                         font_type="Monaco.ttf",
                         length=4,
                         draw_lines=True,
                         n_line=(1, 2),
                         draw_points=True,
                         point_chance=2):
    """
    @todo: 生成验证码图片
    @param size: 图片的大小,格式(宽,高),默认为(120, 30)
    @param chars: 允许的字符集合,格式字符串
    @param img_type: 图片保存的格式,默认为GIF,可选的为GIF,JPEG,TIFF,PNG
    @param mode: 图片模式,默认为RGB
    @param bg_color: 背景颜色,默认为白色
    @param fg_color: 前景色,验证码字符颜色,默认为蓝色#0000FF
    @param font_size: 验证码字体大小
    @param font_type: 验证码字体,默认为 ae_AlArabiya.ttf
    @param length: 验证码字符个数
    @param draw_lines: 是否划干扰线
    @param n_lines: 干扰线的条数范围,格式元组,默认为(1, 2),只有draw_lines为True时有效
    @param draw_points: 是否画干扰点
    @param point_chance: 干扰点出现的概率,大小范围[0, 100]
    @return: [0]: PIL Image实例
    @return: [1]: 验证码图片中的字符串
    """

    width, height = size  # 宽高
    # 创建图形
    img = Image.new(mode, size, bg_color)
    draw = ImageDraw.Draw(img)  # 创建画笔

    def get_chars():
        """生成给定长度的字符串,返回列表格式"""
        return random.sample(chars, length)

    def create_lines():
        """绘制干扰线"""
        line_num = random.randint(*n_line)  # 干扰线条数

        for i in range(line_num):
            # 起始点
            begin = (random.randint(0, size[0]), random.randint(0, size[1]))
            # 结束点
            end = (random.randint(0, size[0]), random.randint(0, size[1]))
            draw.line([begin, end], fill=(0, 0, 0))

    def create_points():
        """绘制干扰点"""
        chance = min(100, max(0, int(point_chance)))  # 大小限制在[0, 100]

        for w in range(width):
            for h in range(height):
                tmp = random.randint(0, 100)
                if tmp > 100 - chance:
                    draw.point((w, h), fill=(0, 0, 0))

    def create_strs():
        """绘制验证码字符"""
        c_chars = get_chars()
        strs = ' %s ' % ' '.join(c_chars)  # 每个字符前后以空格隔开

        font = ImageFont.truetype(font_type, font_size)
        font_width, font_height = font.getsize(strs)

        draw.text(((width - font_width) / 3, (height - font_height) / 3),
                  strs, font=font, fill=fg_color)

        return ''.join(c_chars)

    if draw_lines:
        create_lines()
    if draw_points:
        create_points()
    strs = create_strs()

    # 图形扭曲参数
    params = [1 - float(random.randint(1, 2)) / 100,
              0,
              0,
              0,
              1 - float(random.randint(1, 10)) / 100,
              float(random.randint(1, 2)) / 500,
              0.001,
              float(random.randint(1, 2)) / 500
              ]
    img = img.transform(size, Image.PERSPECTIVE, params)  # 创建扭曲

    img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)  # 滤镜,边界加强(阈值更大)

    return img, strs

猜你喜欢

转载自blog.csdn.net/GoldenKitten/article/details/86500228