原生XMLHTTPResponse,jQuery-Ajax 上传文件;iframe上传图片&预览;图片验证码小案例
原生AJAX
Ajax主要就是使用 【XmlHttpRequest】对象来完成请求的操作,该对象在主流浏览器中均存在(除早起的IE),Ajax首次出现IE5.5中存在(ActiveX控件)
1、XmlHttpRequest对象介绍 (不兼容IE老版本)
x = new XMLHttpRequest()
XmlHttpRequest对象的主要方法:
格式说明:
返回值 方法名(参数,...)
a. void open(String method,String url,Boolen async) 用于创建请求 参数: method: 请求方式(字符串类型),如:POST、GET、DELETE... 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 状态码(整数),如:200、404... f. String statesText 状态文本(字符串),如:OK、NotFound...
测试
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="/static/jquery-2.2.3.min.js"></script> <script src="/static/jquery.cookie.js"></script> </head> <body> <div> <button value="XML_SEND" onclick="xmlSend();">XMLHttpRequest()</button> </div> <script> function xmlSend(){ let xml = new XMLHttpRequest(); //创建XMLHttpResquest对象 xml.open('POST','/ajax_original/'); //设置请求地址 xml.setRequestHeader('X-CSRFtoken',$.cookie('csrftoken') ); //设置请求头 {#xml.send(); //发起请求#} xml.onreadystatechange = function () { //状态改变 console.log('xml 实例readyState状态改变:',xml.readyState); if (xml.readyState === 4) { console.log('收到返回值字符串:',xml.responseText); console.log('转换为对象:',JSON.parse(xml.responseText)); console.log('服务端返回status状态码:',xml.status); console.log('服务端返回statusText文本:',xml.statusText); } }; //测试发送body数据,需要设置请求头Content-Type(告知服务器数据格式,服务器用对应的方式解析) xml.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); xml.send('name=name1; pwd=123'); } </script> </body> </html>
django-views.py函数处理
from django.shortcuts import HttpResponse from django.views import View import json # Create your views here. class AjaxOrnginal(View): def get(self,request): return render(request, 'ajax_templates/original_xmlHttpRequest.html') def post(self, request): print(request.POST) ret = {'code': True, 'data': None} # return HttpResponse(json.dumps(ret)) return HttpResponse(json.dumps(ret),status=404, reason="Oh Fuck ! EveryThing is None")
2、兼容IE老版本XmlHttpRequest对象
<body> <h1>XMLHttpRequest - Ajax请求</h1> <input type="button" onclick="XmlGetRequest();" value="Get发送请求" /> <input type="button" onclick="XmlPostRequest();" value="Post发送请求" /> <script src="/statics/jquery-1.12.4.js"></script> <script type="text/javascript"> function GetXHR(){ var xhr = null; if(XMLHttpRequest){ xhr = new XMLHttpRequest(); }else{ xhr = new ActiveXObject("Microsoft.XMLHTTP"); } return xhr; } function XhrPostRequest(){ var xhr = GetXHR(); // 定义回调函数 xhr.onreadystatechange = function(){ if(xhr.readyState == 4){ // 已经接收到全部响应数据,执行以下操作 var data = xhr.responseText; console.log(data); } }; // 指定连接方式和地址----文件方式 xhr.open('POST', "/test/", true); // 设置请求头 xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset-UTF-8'); // 发送请求 xhr.send('n1=1;n2=2;'); } function XhrGetRequest(){ var xhr = GetXHR(); // 定义回调函数 xhr.onreadystatechange = function(){ if(xhr.readyState == 4){ // 已经接收到全部响应数据,执行以下操作 var data = xhr.responseText; console.log(data); } }; // 指定连接方式和地址----文件方式 xhr.open('get', "/test/", true); // 发送请求 xhr.send(); } </script> </body>
"伪"AJAX
由于HTML标签的iframe标签具有局部加载内容的特性,所以可以使用其来伪造Ajax请求。
<form action="/ajax_original/" method="post" target="ifra"> <iframe name="ifra" src="https://www.baidu.com"></iframe> <input type="text" name="username"> <input type="text" name="email"> <input type="submit" value="伪Ajax提交"> </form>
ifram对象为一个小的新html标签窗口,获取ifram对象的内容需要从这个子窗口document对象
$("#ifra").contents().find('body').text()
试用场景:
普通字符串、字典数据:优先使用用Ajax,其次XMLHttpRequest,再次考虑iframe
上传文件的三种方法
1、原生XMLHttpRequest ---- 上传文件
依赖:new FormData(); 对象,
html前端:XMLHttpRequest()
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="/static/jquery-2.2.3.min.js"></script> <script src="/static/jquery.cookie.js"></script> </head> <body> <div class="upload_div"> <input type="file" value="上传" class="file" id="original_upload_input" name="file1"> </div> <div> <input type="button" onclick="upLoadFile();" value="上传"> </div> <script> function upLoadFile(){ let file_obj = document.getElementById('original_upload_input').files[0]; let fd = new FormData(); fd.append('username', 'root'); fd.append('file1', file_obj); let xhr = new XMLHttpRequest(); xhr.open('post', '/xhr_upload/'); xhr.setRequestHeader('X-CSRFtoken',$.cookie('csrftoken') ); //设置请求头 xhr.onreadystatechange = function () { //状态改变 console.log('xml 实例readyState状态改变:',xhr.readyState); if (xhr.readyState === 4) { console.log('收到返回值字符串:',xhr.responseText); } }; xhr.send(fd); } </script> </body> </html>
django views函数后台处理
from django.shortcuts import render from django.shortcuts import HttpResponse from django.views import View import json # Create your views here. class XhrUpload(View): def get(self,request): return render(request, 'ajax_templates/original_upload.html') def post(self, request): file = request.FILES.get('file1') with open(file.name, 'wb') as f: for item in file.chunks(): f.write(item) return HttpResponse('upload ok')
2、jQuery ---- 上传文件
仍然使用new FormData(); 对象
需增加特殊设置:
processData:false, // tell jQuery not to process the data:不要转义数据
contentType:false, // tell jQuery not to set contentType:不要设置请求头
let file_obj = document.getElementById('original_upload_input').files[0]; // 仍然要依赖于FormData()对象,并且要设置processData和contentType参数 let fd = new FormData(); fd.append('username', 'root'); fd.append('file1', file_obj); //使用ajax 上传数据 $.ajax({ url:'/xhr_upload/', type: 'POST', data:fd, headers:{'X-CSRFtoken': $.cookie('csrftoken')}, processData:false, // tell jQuery not to process the data:不要转义数据 contentType:false, // tell jQuery not to set contentType:不要设置请求头 success:function (arg,a1,a2) { console.log(arg); //返回字符串 console.log(a1); //状态文本 console.log(a2); //XHR对象 } }) }
3、ifram 上传文件
优势:可以将上传后的返回值信息做成图片预览等功能。
html
<form action="/xhr_upload/" method="post" enctype="multipart/form-data" target="ifram1"> {% csrf_token %} <iframe id="ifram1" name="ifram1"></iframe> <input type="file" name="file1"> <input type="submit" onclick="iframSubmit();" value="Form提交(target=ifram1)"> </form>
ajax 从iframe 加载成功后 获取返回值:$('#iframe标签').load(function(){xxxxxx; ......; })
function iframSubmit() { //上传成功,iframe标签加载返回值成功,从标签中获取元素 $('#ifram1').load(function () { //获取返回值, let recv = $('#ifram1').contents().find('body').text(); console.log(recv); }) }
django views.py函数处理
class XhrUpload(View): def get(self,request): return render(request, 'ajax_templates/original_upload.html') def post(self, request): file = request.FILES.get('file1') with open(file.name, 'wb') as f: for item in file.chunks(): f.write(item) ret = HttpResponse('upload ok') ret['X-Frame-Options'] = 'ALLOW' return ret
图片预览版本iframe:
步骤说明:当iframe标签加载成功.onload(function(){})时,获取iframe标签中 的返回值。返回值中包含图片uri, 通过组合'/' + uri 获取图片路径,创建img标签,在浏览器中增加此标签实现图片自动get展示
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="/static/jquery-2.2.3.min.js"></script> <script src="/static/jquery.cookie.js"></script> <style> .hide{ display: block;} </style> </head> <body> <div> <form action="/iframe_upload/" method="post" enctype="multipart/form-data" target="ifram1"> {% csrf_token %} <iframe id="ifram1" name="ifram1" class="hide"></iframe> <input type="file" name="file1"> <input type="submit" onclick="iframSubmit();" value="Form提交(target=ifram1)"> </form> </div> <div id="preview_div"></div> <script> //上传成功,iframe标签加载返回值成功,从标签中获取元素 $('#ifram1').load(function () { //获取返回值,返回的是json.dumps()序列化的字符串也可以获取 let recv = $('#ifram1').contents().find('body').text(); let data = JSON.parse(recv); console.log(data); //获取返回的图片URI,并生成图片标签 let imgTag = document.createElement('img'); imgTag.src = "/" + data.uri; //清空已有img图片标签 $('#preview_div').empty(); //添加dom元素 $('#preview_div').append(imgTag); }); </script> </body> </html>
如需选择文件后自动上传,js绑定onchange事件实现上传:
<head> <meta charset="UTF-8"> <title>Title</title> <script src="/static/jquery-2.2.3.min.js"></script> <script src="/static/jquery.cookie.js"></script> <style> .hide{ display: none;} </style> </head> <body> <div> <form id="upload_form" action="/iframe_upload/" method="post" enctype="multipart/form-data" target="ifram1"> {% csrf_token %} <iframe id="ifram1" name="ifram1" class="hide"></iframe> <input type="file" name="file1" onchange="upLoadFile()"> <input type="submit" onclick="iframSubmit();" value="Form提交(target=ifram1)"> </form> </div> <div id="preview_div"></div> <script> //选择文件后自动上传 function upLoadFile(){ $('#upload_form').submit() } //上传成功,iframe标签加载返回值成功,从标签中获取元素 $('#ifram1').load(function () { //获取返回值,返回的是json.dumps()序列化的字符串也可以获取 let recv = $('#ifram1').contents().find('body').text(); let data = JSON.parse(recv); console.log(data); //获取返回的图片URI,并生成图片标签 let imgTag = document.createElement('img'); imgTag.src = "/" + data.uri; //清空已有img图片标签 $('#preview_div').empty(); //添加dom元素 $('#preview_div').append(imgTag); }); </script> </body>
ifram 优势:在所有浏览器都支持,不存在兼容性问题。而XMLHTTPRequest低版本IE浏览器不支持。
应用案例(点击更换头像):
说明:input标签 属性title='xxxx' 为设置鼠标移入的时候显示的提示语设置
原理:input标签100%占用div;并且透明度为0 即肉眼不可见。img标签和<input type="file">标签重叠在底层显示。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="/static/jquery-2.2.3.min.js"></script> <script src="/static/jquery.cookie.js"></script> <style> .hide{ display: none;} .preview_div{ position: relative; overflow: hidden; display: inline-block; } .head_portrait{ width: 108px; } .head_img { position: absolute; top: 0; bottom: 0; width: 100%; opacity: 0; z-index: 2; } </style> </head> <body> <div> <form id="upload_form" action="/iframe_upload/" method="post" enctype="multipart/form-data" target="ifram1"> {% csrf_token %} <iframe id="ifram1" name="ifram1" class="hide"></iframe> <div id="preview_div" class="preview_div"> <img id="head_portrait" class="head_portrait" src="/static/images/default.jpg" > <input class="head_img" type="file" name="file1" title="点击上传头像" onchange="upLoadFile()" > </div> <input type="submit" onclick="iframSubmit();" value="Form提交(target=ifram1)"> </form> </div> <script> //选择文件后自动上传 function upLoadFile(){ $('#upload_form').submit() } //上传成功,iframe标签加载返回值成功,从标签中获取元素 $('#ifram1').load(function () { //获取返回值,返回的是json.dumps()序列化的字符串也可以获取 let recv = $('#ifram1').contents().find('body').text(); let data = JSON.parse(recv); console.log(data); // 在原有图片上面更新 let uri = "/" + data.uri; $('#head_portrait').attr('src', uri); </script> </body> </html>
-----------------------------------------------------------
图片验证码案例
验证码图片生成
工具:pip3 install pillow
utils/check_code.py
#!/usr/bin/env python # -*- coding:utf-8 -*- import random # pip3 install pillow 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
django views.py 后端使用如上check_code.py 生成图片
from django.shortcuts import render from django.shortcuts import HttpResponse from django.views import View import json import os from io import BytesIO from utils.check_code import create_validate_code # Create your views here. # 生成图片验证码并将对应字符串存储到session中 class CheckCodeImage(View): def get(self, request): # from io import BytesIO ,开辟内存空间,用来存储字节流 stream = BytesIO() # 使用自定义函数创建验证码图片&字符串 img, code_str = create_validate_code() # 将图片保存到内存的字节流中 img.save(stream, 'PNG') # 将验证码字符串保存到session中 request.session['CheckCode'] = code_str # 将图片字节流返回给客户端:HttpResponse()可以返回字节 return HttpResponse(stream.getvalue()) # 登录请求校验验证码 class LoginCaptch(View): def get(self, request): return render(request, 'ajax_templates/login_iframe_captcha.html') def post(self, request): if request.session['CheckCode'] == request.POST.get('CheckCode'): print('验证码正确') else: print('验证码错误:',request.session['CheckCode'].upper(),request.POST.get('CheckCode')) return render(request, 'ajax_templates/login_iframe_captcha.html')
urls.py
url(r'iframe_login_captch/', ajax_views.LoginCaptch.as_view()), url(r'^check_code.html$', ajax_views.CheckCodeImage.as_view()),
login_iframe_captcha.html 前端展示验证码图片
点击图片本身,自动刷新验证码:请求url增加? 浏览器检测到url变化,重新发起请求,后端处理不变。
login_iframe_captcha.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .login{ display: inline-block; } </style> </head> <body> <form action="/iframe_login_captch/" method="post"> {% csrf_token %} <div class="login"> <div class="form-group"> <label for="username">用户名</label> <input type="text" class="form-control" id="username" name="username" placeholder="请输入用户名" > </div> <div class="form-group"> <label for="password">密码</label> <input type="password" class="form-control" id="password" name="pwd" placeholder="请输入密码" > </div> <div class="form-group"> <label for="captcha">验证码</label> <input type="text" class="form-control" id="captcha" name="CheckCode" placeholder="验证码" > </div> <button type="submit">登 陆</button> </div> </form> <div> <img src="/check_code.html" onclick="changeCheckCode(this)"> </div> <script> //点击图片改变图片验证码: 请求url增加? 浏览器检测到url变化,重新发起请求,后端处理不变。 function changeCheckCode(ths) { ths.src = ths.src + "?"; } </script> </body> </html>
---------------------------------------------------------------------
posted on 2020-04-18 16:07 zhangmingda 阅读(282) 评论(0) 编辑 收藏 举报