web框架 之 Tornado
初识 Tornado :
tornado web server 是使用python编写出来的一个轻量级、高可伸缩性和非阻塞IO的Web服务器软件,其特点是采用epoll非阻塞IO,相应快速,可处理数千并发连接,特别适用于实时的Web服务。
概述:
Tornado 是 FriendFeed 使用的可扩展的非阻塞式 web 服务器及其相关工具的开源版本。这个 Web 框架看起来有些像web.py 或者 Google 的 webapp,不过为了能有效利用非阻塞式服务器环境,这个 Web 框架还包含了一些相关的有用工具 和优化。
Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快。得利于其 非阻塞的方式和对 epoll 的运用,Tornado 每秒可以处理数以千计的连接,这意味着对于实时 Web 服务来说,Tornado 是一个理想的 Web 框架。我们开发这个 Web 服务器的主要目的就是为了处理 FriendFeed 的实时功能 ——在 FriendFeed 的应用里每一个活动用户都会保持着一个服务器连接。(关于如何扩容 服务器,以处理数以千计的客户端的连接的问题,请参阅 C10K problem。)
下载安装:
pip3 install tornado
源码安装
https://pypi.python.org/packages/source/t/tornado/tornado-4.3.tar.gz
快速上手:
import tornado.ioloop 首先导入模块 import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world") application = tornado.web.Application([ (r"/index", MainHandler), #路由映射(路由系统) ]) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
执行过程:
- 第一步:执行脚本,监听 8888 端口
- 第二步:浏览器客户端访问 /index --> http://127.0.0.1:8888/index
- 第三步:服务器接受请求,并交由对应的类处理该请求
- 第四步:类接受到请求之后,根据请求方式(post / get / delete ...)的不同调用并执行相应的方法
- 第五步:方法返回值的字符串内容发送浏览器
import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): # self.render("s1.html") #render方法,表示会自动找到文件并打开,返回给你 #render找的时候默认从当前的目录下面去找,如果想让其从别的地方找,我们就可以 #为其做一个settings配置(也可以把绝对路径写上去), self.write("Hello, world") settings={ #如果想让配置文件生效,需要在下面application后面加一个**settings "tempalte_path":"xxx", #模板路径的匹配,其中xxx为放HTML的文件夹 "static_path":"xxx" #静态文件的配置(静态文件就是css和JavaScript),其中xxx为存放静态文件的文件夹 } #路由映射(路由系统) application = tornado.web.Application([ (r"/index", MainHandler), #检测用户的url是否匹配,如果匹配则,执行其后面类中的莫一种方法 #同一个url以不同的方式访问,执行不同的方法 ],**settings) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
二、路由系统 (application)
路由系统其实就是 url 和 类 的对应关系,这里不同于其他框架,其他很多框架均是 url 对应 函数,Tornado中每个url对应的是一个类。
application=tornado.web.Application([
(r'/index',MainHandler)
],**settings)
内部在执行的时候执行了两个方法__init__方法和self.add_handlers(".*$", handlers)方法{源码后期解析Tornado时补充}
这个add_handlers默认传输的".*$" 就是www,他内部生成的路由映射的时候相当于(二级域名的方式)下图:
我们可以通过application.add_handlers,添加一个“shuaige.com”,他会生成一个类似下面的对应关系shuaige.*
如果匹配的是shuaige他会去"shuaige"里去找对应关系,如果没有匹配默认就去.*,他这个就类似Django中的URL分类!~~
application = tornado.web.Application([ (r"/index", MainHandler), ]) application.add_handlers("shuaige.com",([ (r"/index", MainHandler), ]) )
路由系统其实就是 url 和 类 的对应关系,这里不同于其他框架,其他很多框架均是 url 对应 函数,Tornado中每个url对应的是一个类。
#!/usr/bin/env python #-*- coding:utf-8 -*- __author__ = 'luotianshuai' import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world") class Shuaige(tornado.web.RedirectHandler): def get(self): self.write("This is shuaige web site,hello!") application = tornado.web.Application([ (r"/index", MainHandler), ]) application.add_handlers("shuaige.com",([ #二级路由 (r"/index", Shuaige), ]) ) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
模板:
Tornao中的模板语言和django中类似,模板引擎将模板文件载入内存,然后将数据嵌入其中,最终获取到一个完整的字符串,再将字符串返回给请求者。
Tornado 的模板支持“控制语句”和“表达语句”,控制语句是使用 {%
和 %}
包起来的 例如 {% if len(items) > 2 %}
。表达语句是使用 {{
和 }}
包起来的,例如 {{ items[0] }}
。
控制语句和对应的 Python 语句的格式基本完全相同。我们支持 if
、for
、while
和 try
,这些语句逻辑结束的位置需要用 {% end %}
做标记。还通过 extends
和 block
语句实现了模板继承。这些在 template
模块 的代码文档中有着详细的描述。
注:在使用模板前需要在setting中设置模板路径:"template_path" : "views"
settings = { 'template_path':'views', #设置模板路径,HTML文件放置views文件夹中 'static_path':'static', # 设置静态模板路径,css,JS,Jquery等静态文件放置static文件夹中 'static_url_prefix': '/sss/', #导入时候需要加上/sss/,例如<script src="/sss/jquery-1.9.1.min.js"></script> 自己去找该js文件 'cookie_secret': "asdasd", #cookie生成秘钥时候需提前生成随机字符串,需要在这里进行渲染 'xsrf_cokkies':True, #允许CSRF使用 } application = tornado.web.Application([ (r'/index',IndexHandler), # 路由映射 路由系统 ],**settings)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .pg-header{ height: 48px; background-color: darkcyan; } .pg-footer{ height: 100px; background-color:beige; } </style> </head> <body> <div class="pg-header"> </div> <div class="pg-contet"> {% block body %} {% end %} </div> <div class="pg-footer">AAAAAAAAAA</div> <script src="{{static_url('js/jquery-1.8.2.min.js')}}"></script> {% block js %} {% end %} </body> </html>
{% extends '../master/layout.html'%} {% block body %} <h1>Index</h1> {% include "../include/form.html" %} {% end %} {% block js %} {% end %}
模板中for循环的语法
{% extends '../master/layout.html'%} {% block body %} <h1>Fuck</h1> {% include "../include/form.html" %} {% include "../include/form.html" %} {% include "../include/form.html" %} {% include "../include/form.html" %} {% end %}
<form action="/"> <input type="text"/> <input type="submit"/> </form> <ul> {% for item in list_info %} <li>{{item}}</li> {% end %} </ul>index
在模板中默认提供了一些函数、字段、类以供模板使用: escape: tornado.escape.xhtml_escape 的別名 xhtml_escape: tornado.escape.xhtml_escape 的別名 url_escape: tornado.escape.url_escape 的別名 json_encode: tornado.escape.json_encode 的別名 squeeze: tornado.escape.squeeze 的別名 linkify: tornado.escape.linkify 的別名 datetime: Python 的 datetime 模组 handler: 当前的 RequestHandler 对象 request: handler.request 的別名 current_user: handler.current_user 的別名 locale: handler.locale 的別名 _: handler.locale.translate 的別名 static_url: for handler.static_url 的別名 xsrf_form_html: handler.xsrf_form_html 的別名
分享一个前后端交互实例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="sss/commons.css" > </head> <body> <h1>提交数据</h1> <form method="get" action="/index"> <input type="text" name="xxx" /> <input type="submit" value="提交" /> </form> <h1>展示内容</h1> <h3>{{npm}}</h3> <h3>{{ func(npm) }}</h3> {% module custom(123) %} <ul> {% for item in xxxooo %} {% if item == "alex" %} <li style="color: red">{{item}}</li> {% else %} <li>{{item}}</li> {% end %} {% end %} </ul> <script src="/sss/oldboy.js"></script> </body> </html>
#!/usr/bin/env python #-*- coding:utf-8 -*- import tornado.ioloop import tornado.web import uimethod as mt import uimodule as md INPUTS_LIST = ['alex'] class MainHandler(tornado.web.RequestHandler): def get(self): name = self.get_argument("xxx",None) if name: INPUTS_LIST.append(name) self.render('s1.html',npm="NPM888",xxxooo = INPUTS_LIST) # def post(self,*args,**kwargs): # name = self.get_argument("xxx",None) # INPUTS_LIST.append(name) # self.render('s1.html',npm="NPM888",xxxooo = INPUTS_LIST) settings={ 'template_path':'tpl', 'static_path':'static', 'static_url_prefix':'/sss/', 'ui_methods':mt, 'ui_modules':md } application=tornado.web.Application([ (r'/index',MainHandler) ],**settings) if __name__ == '__main__': application.listen(8888) tornado.ioloop.IOLoop.instance().start()
模板语言有三类:
1、{{npm}}-------------self.render("s1.html",npm = "NPM888")
2、代码块的方式
{% for item in xxxooo %} ---- self.render('s1.html',xxxooo = INPUTS_LIST)
{% if item == "alex" %}
<li style="color: red">{{item}}</li>
{% else %}
<li>{{item}}</li>
{% end %}
{% end %}
3、自定义
def func(self,arg):
return "12345"
#!/usr/bin/env python # -*- coding:utf-8 -*- from tornado.web import UIModule from tornado import escape class custom(UIModule): def render(self, *args, **kwargs): return escape.xhtml_escape('<h1>哈哈哈</h1>')
cookie
Cookie是当你浏览某网站时,网站存储在你机器上的一个小文本文件,它记录了你的用户ID,密码、浏览过的网页、停留的时间等信息,当你再次来到该网站时,网站通过读取Cookie,得知你的相关信息,就可以做出相应的动作,如在页面显示欢迎你的标语,或者让你不用输入ID、密码就直接登录等。
#!/usr/bin/env/python # -*- coding:utf-8 -*- import tornado.web class IndexHandler(tornado.web.RequestHandler): def get(self): print(self.cookies) #获取http请求中携带的浏览器中的所有cookie print(self.get_cookie("k1")) # 获取浏览器中的cooki self.set_cookie('k1','111') #为浏览器设置cookie self.render('index.html') settings = { 'template_path':'views', 'static_path':'static' } application = tornado.web.Application([ (r'/index',IndexHandler), ],**settings) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script> function setCookie(name,value,expires) { var current_date = new Date(); current_date.setSeconds(current_date.getSeconds()+5); document.cookie = name + '='+ value +';expires='+ current_date.toUTCString(); } </script> </body> </html>
2、加密cookie(签名)
Cookie 很容易被恶意的客户端伪造。加入你想在 cookie 中保存当前登陆用户的 id 之类的信息,你需要对 cookie 作签名以防止伪造。Tornado 通过 set_secure_cookie 和 get_secure_cookie 方法直接支持了这种功能。 要使用这些方法,你需要在创建应用时提供一个密钥,名字为 cookie_secret。 你可以把它作为一个关键词参数传入应用的设置中
签名Cookie的本质是:
写cookie过程:
- 将值进行base64加密
- 对除值以外的内容进行签名,哈希算法(无法逆向解析)
- 拼接 签名 + 加密值
v1 = base64(v1)
k1 = v1 | 加密串(md5(v1+时间戳+自定义字符串)) | 时间戳
读cookie过程:
- 读取 签名 + 加密值
- 对签名进行验证
- base64解密,获取值内容
#!/usr/bin/env/python # -*- coding:utf-8 -*- import tornado.web import hashlib import time xin = {} #创建一个空字典 class IndexHandler(tornado.web.RequestHandler): def get(self): if self.get_argument('u',None) in['kai','xin']: obj = hashlib.md5() obj.update(bytes(str(time.time()),encoding="utf-8")) random_str = obj.hexdigest() xin[random_str] = {} xin[random_str]['k1']=123 xin[random_str]['k2']=self.get_argument('u',None)+'parents' xin[random_str]['is_login']=True self.set_cookie('iiii',random_str) else: self.write('请先登录') class ManagerHandler(tornado.web.RequestHandler): def get(self): random_str = self.get_cookie('iiii') xin_user_info = xin.get(random_str,None) if not xin_user_info: self.redirect('/index') else: if xin_user_info.get('is_login',None): time = "%s - %s " %(xin_user_info.get('k1',""),xin_user_info.get('k2',"")) self.write(time) else: self.redirect('/index') settings = { 'template_path':'views', 'static_path':'static' } application = tornado.web.Application([ (r'/index',IndexHandler), (r'/manger',ManagerHandler), ],**settings) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
五、Session(依赖于cookie)
由于cookie中需要保存客户的很多信息,而且如果信息很多的话,服务端与客户端交互的时候也浪费流量,所以我们需要用很少的一段字符串来保存很多的信息,这就是我们所要引进的session。
cookie 和session 的区别:
1、cookie数据存放在客户的浏览器上,session数据放在服务器上。
2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗 考虑到安全应当使用session。
3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能 考虑到减轻服务器性能方面,应当使用COOKIE。
4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。
5、所以个人建议: 将登陆信息等重要信息存放为SESSION 其他信息如果需要保留,可以放在COOKIE中
#!/usr/bin/env/python # -*- coding:utf-8 -*- import tornado.web container = {} # container = { # # "第一个人的随机字符串":{}, # # "第一个人的随机字符串":{'k1': 111, 'parents': '你'}, # } class Session: def __init__(self, handler): self.handler = handler self.random_str = None def __genarate_random_str(self): import hashlib import time obj = hashlib.md5() obj.update(bytes(str(time.time()), encoding='utf-8')) random_str = obj.hexdigest() return random_str def __setitem__(self, key, value): # 在container中加入随机字符串 # 定义专属于自己的数据 # 在客户端中写入随机字符串 # 判断,请求的用户是否已有随机字符串 if not self.random_str: random_str = self.handler.get_cookie('__kakaka__') if not random_str: random_str = self.__genarate_random_str() container[random_str] = {} else: # 客户端有随机字符串 if random_str in container.keys(): pass else: random_str = self.__genarate_random_str() container[random_str] = {} self.random_str = random_str # self.random_str = asdfasdfasdfasdf container[self.random_str][key] = value self.handler.set_cookie("__kakaka__", self.random_str) def __getitem__(self, key): # 获取客户端的随机字符串 # 从container中获取专属于我的数据 # 专属信息【key】 random_str = self.handler.get_cookie("__kakaka__") if not random_str: return None # 客户端有随机字符串 user_info_dict = container.get(random_str,None) if not user_info_dict: return None value = user_info_dict.get(key, None) return value class BaseHandler(tornado.web.RequestHandler): def initialize(self): self.session = Session(self) class IndexHandler(BaseHandler): def get(self): if self.get_argument('u',None) in ['alex','eric']: self.session['is_login'] = True self.session['name'] =self.get_argument('u',None) print(container) else: self.write('请你先登录') class MangerHandler(BaseHandler): def get(self): # s = Session(self) # val = s.get_value('is_login') val = self.session['is_login'] if val: # self.write(s.get_value('name')) self.write(self.session['name']) else: self.write('登录失败') class LoginHandler(BaseHandler): def get(self,*args,**kwargs): self.render('login.html',status="") def post(self, *args, **kwargs): user = self.get_argument('user',None) pwd = self.get_argument('pwd',None) code = self.get_argument('code',None) check_code = self.session['CheckCode'] if code.upper() == check_code.upper(): self.write('验证码正确') else: self.render('login.html',status ='验证码错误') settings = { 'template_path':'views', 'statics_path':'static', } application = tornado.web.Application([ (r'/index',IndexHandler), (r'/manger',MangerHandler), (r'/login',LoginHandler), ],**settings) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
#!/usr/bin/env python # -*- coding:utf-8 -*- #!/usr/bin/env python import tornado.web from controllers import home settings = { 'template_path': 'views', # 模板路径的配置 'static_path': "static", # 静态文件 "cookie_secrte": 'uiuoajskfjalsdjf', } # 路由映射,路由系统 application = tornado.web.Application([ (r"/index/(?P<page>\d*)", home.IndexHandler), ], **settings) application.add_handlers('buy.wupeiqi.com$',[ (r"/index/(?P<page>\d*)", buy.IndexHandler), ]) if __name__ == "__main__": # socket运行起来 application.listen(8888) tornado.ioloop.IOLoop.instance().start()
#!/usr/bin/env python # -*- coding:utf-8 -*- class Pagenation: def __init__(self,current_page,all_item,base_url): try: page = int(current_page) except: page = 1 if page < 1: page = 1 all_pager, c = divmod(all_item, 5) if c > 0: all_pager += 1 self.current_page = page self.all_pager = all_pager self.base_url = base_url @property def start(self): return (self.current_page - 1) * 5 @property def end(self): return self.current_page * 5 def string_pager(self): list_page = [] if self.all_pager < 11: s = 1 t = self.all_pager + 1 else: # 总页数大于11 if self.current_page < 6: s = 1 t = 12 else: if (self.current_page + 5) < self.all_pager: s = self.current_page - 5 t = self.current_page + 5 + 1 else: s = self.all_pager - 11 t = self.all_pager + 1 # 首页 first = '<a href="/index/1">首页</a>' list_page.append(first) # 上一页 # 当前页 page if self.current_page == 1: prev = '<a href="javascript:void(0);">上一页</a>' else: prev = '<a href="/index/%s">上一页</a>' % (self.current_page - 1,) list_page.append(prev) for p in range(s, t): # 1-11 if p == self.current_page: temp = '<a class="active" href="/index/%s">%s</a>' % (p, p) else: temp = '<a href="/index/%s">%s</a>' % (p, p) list_page.append(temp) if self.current_page == self.all_pager: nex = '<a href="javascript:void(0);">下一页</a>' else: nex = '<a href="/index/%s">下一页</a>' % (self.current_page + 1,) list_page.append(nex) # 尾页 last = '<a href="/index/%s">尾页</a>' % (self.all_pager,) list_page.append(last) # 跳转 jump = """<input type='text' /><a onclick="Jump('%s',this);">GO</a>""" % ('/index/') script = """<script> function Jump(baseUrl,ths){ var val = ths.previousElementSibling.value; if(val.trim().length>0){ location.href = baseUrl + val; } } </script>""" list_page.append(jump) list_page.append(script) str_page = "".join(list_page) return str_page
#!/usr/bin/env python # -*- coding:utf-8 -*- import tornado.web from commons import pager LIST_INFO = [ {'username': 'alex', "email": "alex3721@163.com"}, ] for i in range(300): temp = {'username': 'alex'+str(i), "email": str(i) + '123@qq.com'} LIST_INFO.append(temp) class IndexHandler(tornado.web.RequestHandler): def get(self, page): obj = pager.Pagenation(page, len(LIST_INFO), '/index/') current_list = LIST_INFO[obj.start:obj.end] str_page = obj.string_pager() self.render('home/index.html', list_info = current_list, current_page = obj.current_page, str_page = str_page) def post(self,page): user = self.get_argument('username') email = self.get_argument('email') temp = {'username': user, 'email': email} LIST_INFO.append(temp) self.redirect("/index/"+page)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .pager a{ display: inline-block; padding: 5px; margin: 3px; background-color: cadetblue; } .pager a.active{ background-color: brown; color: white; } </style> </head> <body> <h1>提交数据</h1> <form method="post" action="/index/{{current_page}}"> <input name="username" type="text" /> <input name="email" type="text" /> <input type="submit" value="提交" /> </form> <h1>显示数据</h1> <table border="1"> <thead> <tr> <th>用户名</th> <th>邮箱</th> </tr> </thead> <tbody> {% for line in list_info %} <tr> <!--<td>{{line['username']}}</td>--> <td>{{ line['username'] }}</td> <td>{{line['email']}}</td> </tr> {% end %} </tbody> </table> <div class="pager"> {% raw str_page %} </div> </body> </html>
tornado 随机验证码
登陆注册的时候,需要验证码的功能,原理为在后台自动创建一张随机图片,然后通过img标签输出到前端。这里我们需要安装一个pillow的模块,相应的生成随机验证代码文件如下,此外还需要一个字体文件
安装图像处理模块:
pip3 install pillow
#!/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
#!/usr/bin/env/python # -*- coding:utf-8 -*- import tornado.web container = {} # container = { # # "第一个人的随机字符串":{}, # # "第一个人的随机字符串":{'k1': 111, 'parents': '你'}, # } class Session: def __init__(self, handler): self.handler = handler self.random_str = None def __genarate_random_str(self): import hashlib import time obj = hashlib.md5() obj.update(bytes(str(time.time()), encoding='utf-8')) random_str = obj.hexdigest() return random_str def __setitem__(self, key, value): # 在container中加入随机字符串 # 定义专属于自己的数据 # 在客户端中写入随机字符串 # 判断,请求的用户是否已有随机字符串 if not self.random_str: random_str = self.handler.get_cookie('__kakaka__') if not random_str: random_str = self.__genarate_random_str() container[random_str] = {} else: # 客户端有随机字符串 if random_str in container.keys(): pass else: random_str = self.__genarate_random_str() container[random_str] = {} self.random_str = random_str # self.random_str = asdfasdfasdfasdf container[self.random_str][key] = value self.handler.set_cookie("__kakaka__", self.random_str) def __getitem__(self, key): # 获取客户端的随机字符串 # 从container中获取专属于我的数据 # 专属信息【key】 random_str = self.handler.get_cookie("__kakaka__") if not random_str: return None # 客户端有随机字符串 user_info_dict = container.get(random_str,None) if not user_info_dict: return None value = user_info_dict.get(key, None) return value class BaseHandler(tornado.web.RequestHandler): def initialize(self): self.session = Session(self) class IndexHandler(BaseHandler): def get(self): if self.get_argument('u',None) in ['alex','eric']: self.session['is_login'] = True self.session['name'] =self.get_argument('u',None) print(container) else: self.write('请你先登录') class MangerHandler(BaseHandler): def get(self): # s = Session(self) # val = s.get_value('is_login') val = self.session['is_login'] if val: # self.write(s.get_value('name')) self.write(self.session['name']) else: self.write('登录失败') class LoginHandler(BaseHandler): def get(self,*args,**kwargs): self.render('login.html',status="") def post(self, *args, **kwargs): user = self.get_argument('user',None) pwd = self.get_argument('pwd',None) code = self.get_argument('code',None) check_code = self.session['CheckCode'] if code.upper() == check_code.upper(): self.write('验证码正确') else: self.render('login.html',status ='验证码错误') class CheckCodeHandler(BaseHandler): def get(self,*args,**kwargs): import io import check_code mstream = io.BytesIO() #创建图片,并写入验证码 img, code = check_code.create_validate_code() #将图片对象写入到mstrem img.save(mstream,'GIF') self.session['CheckCode']=code self.write(mstream.getvalue()) class CsrfHandler(BaseHandler): def get(self,*args,**kwargs): self.render('csrf.html') def post(self, *args, **kwargs): self.write('csrf.post') settings = { 'template_path':'views', 'statics_path':'static', 'xsrf_cookies':True } application = tornado.web.Application([ (r'/index',IndexHandler), (r'/manger',MangerHandler), (r'/login',LoginHandler), (r'/check_code',CheckCodeHandler), (r'/csrf',CsrfHandler) ],**settings) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/csrf" method="post"> {% raw xsrf_form_html() %} <p><input name = 'user' type="text" placeholder=“用户名/></p> <p><input name="pwd" type="text" placeholder="密码"></p> <p> <input name="code" type="text" placeholder="验证码"> <img src="/check_code" onclick="ChangeCode(); id = imgCode"> </p> <input type="submit" value="提交"/> <span style="color: #ac2925"></span> </form> <script src="/static/jquery-1.12.4.js"></script> <script> function ChangeCode() { var code = document.getElementById('imgCode'); code.sre += "?"; } </script> </body> </html>
Xss跨站脚本攻击
class IndexHandler(tornado.web.RequestHandler): def get(self, *args, **kwargs): jump = '''<input type="text"><a onclick = "Jump('%s',this);">GO</a>'''%('/index/') script = ''' <script> function Jump(baseUrl,ths){ var val = ths.previousElementSibling.value; if (val.trim().length > 0){ location.href = baseUrl + val; } } </script> ''' self.render('index.html',jump=jump,script=script) #传入两个前端代码的字符串
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .pager a{ display: inline-block; padding: 5px; margin: 3px; background-color: #00a2ca; } .pager a.active{ background-color: #0f0f0f; color: white; } </style> </head> <body> <div class="pager"> {% raw jump %} {% raw script%} </div> </body> </html>
csrf跨站请求伪造
get请求的时候,会给浏览器发一个id(cookie),浏览器post请求的时候,携带这个id,然后服务端对其做验证,如果没有这个id的话,就禁止浏览器提交内容。下面来看一下在tornado里面怎么设置,首先需要在settings里面配置 'xsrf_cookies': True,如果这样配置的话,浏览器发送post请求的话这样设置之后,Tornado 将拒绝请求参数中不包含正确的_xsrf
值的 post/put/delete 请求,如果没有携带相应的id(session)则会禁止访问。{% raw xsrf_form_html() %}
是新增的,目的就在于实现上面所说的授权给前端以合法请求。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/csrf" method="post"> {% raw xsrf_form_html() %} <input type="button" value="Ajax CSRF" onclick="SubmitCsrf();" /> </form> <script src="/static/jquery-1.12.4.js"></script> <script> function getCookie(name) { var r = document.cookie.match('\\b'+ name + "=([^:]*)\\b"); return r ? r[1]:undefined; } function SubmitCsrf() { var nid = getCookie("_xsrf"); $.post({ url:'/csrf', data:{'k1':'v1','_xsrf':nid}, success:function (callback) { // Ajax请求发送成功有,自动执行 // callback,服务器write的数据 callback=“csrf.post” console.log(callback); } }); } </script> </body> </html>
#!/usr/bin/env/python # -*- coding:utf-8 -*- import tornado.web class CsrfHandler(BaseHandler): def get(self,*args,**kwargs): self.render('csrf.html') def post(self, *args, **kwargs): self.write('csrf.post') settings = { 'template_path':'views', 'statics_path':'static', 'xsrf_cookies':True } application = tornado.web.Application([ (r'/csrf',CsrfHandler) ],**settings) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start(
ajax
为什么使用ajax,局部刷新,减少请求中发送的数据
AJAX,Asynchronous JavaScript and XML (异步的JavaScript和XML),一种创建交互式网页应用的网页开发技术方案。
- 异步的JavaScript:
使用 【JavaScript语言】 以及 相关【浏览器提供类库】 的功能向服务端发送请求,当服务端处理完请求之后,【自动执行某个JavaScript的回调函数】。以上请求和响应的整个过程是【偷偷】进行的,页面上无任何感知。 - XML
XML是一种标记语言,是Ajax在和后台交互时传输数据的格式之一,但是现在使用的很少,基本都是使用json来做数据交换
利用AJAX可以做:
1、注册时,输入用户名自动检测用户是否已经存在。
2、登陆时,提示用户名密码错误
3、删除数据行时,将行ID发送到后台,后台在数据库中删除,数据库删除成功后,在页面DOM中将数据行也删除。
首先来看一下一种用iframe标签模拟ajax请求
#!/usr/bin/env/python # -*- coding:utf-8 -*- import tornado.web IMG_LIST = [] class PicturesHandler(tornado.web.RequestHandler): def get(self, *args, **kwargs): self.render('iframe.html',img_list = IMG_LIST) def post(self, *args, **kwargs): print(self.get_argument('user')) print(self.get_arguments('favor')) file_maetas = self.request.files['hahaha'] for meta in file_maetas: file_name = meta['filename'] import os with open(os.path.join('static','img',file_name),'wb')as up: up.write(meta["body"]) IMG_LIST.append(file_name) self.write('{"status":1,"message":"mmm"}') settings = { 'template_path':"views", 'static_path':'static', } application = tornado.web.Application([ (r'/iframe',PicturesHandler) ],**settings) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div> <p>请输入要加载的地址:<span id="currentTime"></span></p> <p> <input id="url" type="text" /> <input type="button" value="刷新" onclick="LoadPage();"> </p> </div> <div> <h3>加载页面位置:</h3> <iframe id="iframePosition" style="width: 100%;height: 500px;"></iframe> </div> <script type="text/javascript"> window.onload= function(){ var myDate = new Date(); document.getElementById('currentTime').innerText = myDate.getTime(); }; function LoadPage(){ var targetUrl = document.getElementById('url').value; document.getElementById("iframePosition").src = targetUrl; } </script> </body> </html>
Ajax主要就是使用 【XmlHttpRequest】对象来完成请求的操作,该对象在主流浏览器中均存在(除早起的IE),Ajax首次出现IE5.5中存在(ActiveX控件)
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...
跨浏览器支持
XmlHttpRequest IE7+, Firefox, Chrome, Opera, etc.
- ActiveXObject("Microsoft.XMLHTTP") IE6, IE5
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <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> </html>
jQuery其实就是一个JavaScript的类库,其将复杂的功能做了上层封装,使得开发者可以在其基础上写更少的代码实现更多的功能。
- jQuery 不是生产者,而是大自然搬运工。
- jQuery Ajax本质 XMLHttpRequest 或 ActiveXObject
注:2.+版本不再支持IE9以下的浏览器
jQuery.get(...) 所有参数: url: 待载入页面的URL地址 data: 待发送 Key/value 参数。 success: 载入成功时回调函数。 dataType: 返回内容格式,xml, json, script, text, html jQuery.post(...) 所有参数: url: 待载入页面的URL地址 data: 待发送 Key/value 参数 success: 载入成功时回调函数 dataType: 返回内容格式,xml, json, script, text, html jQuery.getJSON(...) 所有参数: url: 待载入页面的URL地址 data: 待发送 Key/value 参数。 success: 载入成功时回调函数。 jQuery.getScript(...) 所有参数: url: 待载入页面的URL地址 data: 待发送 Key/value 参数。 success: 载入成功时回调函数。 jQuery.ajax(...) 部分参数: url:请求地址 type:请求方式,GET、POST(1.9.0之后用method) headers:请求头 data:要发送的数据 contentType:即将发送信息至服务器的内容编码类型(默认: "application/x-www-form-urlencoded; charset=UTF-8") async:是否异步 timeout:设置请求超时时间(毫秒) beforeSend:发送请求前执行的函数(全局) complete:完成之后执行的回调函数(全局) success:成功之后执行的回调函数(全局) error:失败之后执行的回调函数(全局) accepts:通过请求头发送给服务器,告诉服务器当前客户端课接受的数据类型 dataType:将服务器端返回的数据转换成指定类型 "xml": 将服务器端返回的内容转换成xml格式 "text": 将服务器端返回的内容转换成普通文本格式 "html": 将服务器端返回的内容转换成普通文本格式,在插入DOM中时,如果包含JavaScript标签,则会尝试去执行。 "script": 尝试将返回值当作JavaScript去执行,然后再将服务器端返回的内容转换成普通文本格式 "json": 将服务器端返回的内容转换成相应的JavaScript对象 "jsonp": JSONP 格式 使用 JSONP 形式调用函数时,如 "myurl?callback=?" jQuery 将自动替换 ? 为正确的函数名,以执行回调函数 如果不指定,jQuery 将自动根据HTTP包MIME信息返回相应类型(an XML MIME type will yield XML, in 1.4 JSON will yield a JavaScript object, in 1.4 script will execute the script, and anything else will be returned as a string converters: 转换器,将服务器端的内容根据指定的dataType转换类型,并传值给success回调函数 $.ajax({ accepts: { mycustomtype: 'application/x-some-custom-type' }, // Expect a `mycustomtype` back from server dataType: 'mycustomtype' // Instructions for how to deserialize a `mycustomtype` converters: { 'text mycustomtype': function(result) { // Do Stuff return newresult; } }, });
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <p> <input type="button" onclick="JqSendRequest();" value='Ajax请求' /> </p> <script type="text/javascript" src="/c/static/jquery-1.9.1.min.js"></script> <script> function JqSendRequest(){ $.ajax({ url: "http://c2.com:8000/test/", type: 'GET', data:{"k1":"v1"}, //向服务端发送内容,服务端可以通过self.get_argument("k1")获取 dataType: 'text', success: function(data, statusText, xmlHttpRequest){ console.log(data,statusText); } }) } </script> </body> </html>
四、跨域AJAX
1.什么引起了ajax跨域不能的问题
ajax本身实际上是通过XMLHttpRequest对象来进行数据的交互,而浏览器出于安全考虑,不允许js代码进行跨域操作,所以会警告。
特别的:由于同源策略是浏览器的限制,所以请求的发送和响应是可以进行,只不过浏览器不接受罢了。
浏览器同源策略并不是对所有的请求均制约:
- 制约: XmlHttpRequest
- 不叼: img、iframe、script等具有src属性的标签
跨域,跨域名访问,如:http://www.c1.com 域名向 http://www.c2.com域名发送请求。
1、JSONP实现跨域请求(利用script块的特性)
JSONP(JSONP - JSON with Padding是JSON的一种“使用模式”),利用script标签的src属性(浏览器允许script标签跨域)
#!/usr/bin/env/python # -*- coding:utf-8 -*- import tornado.web class IndexHandler(tornado.web.RequestHandler): def get(self): self.render('index.html') def post(self, *args, **kwargs): self.write('t1.post') settings = { 'template_path':'views', 'static_path':'static', } application = tornado.web.Application([ (r'/index',IndexHandler), ],**settings) if __name__ == "__main__": application.listen(8001) tornado.ioloop.IOLoop.instance().start()
#!/usr/bin/env/python # -*- coding:utf-8 -*- import tornado.web class IndexHandler(tornado.web.RequestHandler): def get(self): callback = self.get_argument('callback') self.write('%s([11,22,33]);'% callback) # self.write('func([11,22,33])') def post(self, *args, **kwargs): self.write('t2.post') settings = { 'template_path':'views', 'static_path':'static', } application = tornado.web.Application([ (r'/index',IndexHandler), ],**settings) if __name__ == "__main__": application.listen(8002) tornado.ioloop.IOLoop.instance().start()
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 </head> 7 <body> 8 <input type="button" value="Ajax" onclick="DoAjax();"> 9 <input type="button" value="JsonpAjax" onclick="JsonpAjax();"> 10 <script src="/statics/jquery-1.12.4.js"></script> 11 <script> 12 13 function func(arg) { 14 console.log(arg) 15 } 16 function DoAjax() { 17 $.ajax({ 18 url: 'http://w2.com:8002/index', 19 type: 'POST', 20 data: {'k1': 'v1'}, 21 success:function (arg) { 22 console.log(arg) 23 } 24 }); 25 } 26 27 function JsonpAjax() { 28 29 // var tag = document.createElement("script"); 30 // tag.src = 'http://127.0.0.1:8002/index?callback=func'; 31 // document.head.appendChild(tag); 32 // document.head.removeChild(tag); 33 34 $.ajax({ 35 url:'http://127.0.0.1:8002/index', 36 dataType: 'jsonp', 37 jsonp: 'callback', 38 jsonpCallBack: 'func' 39 }) 40 } 41 </script> 42 </body> 43 </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <input type="button" value="Ajax" onclick="DoAjax();"/> <input type="button" value="JsonpAjaxJX" onclick="JsonpAjaxJX();"/> <script src="/static/jquery-1.12.4.js"></script> <script src="127.0.0.1:8002/index/static/jquery.cookie.js"></script> <script> function func(arg) { console.log(arg) } function DoAjax() { $.ajax({ url:'http://127.0.0.1:8002/index', type:'post', data:{'k1':'v1'}, success:function (arg) { console.log(arg); } }) } function list(dict) { console.log(dict) } function JsonpAjaxJX() { $.ajax({ url:'http://www.jxntv.cn/data/jmd-jxtv2.html?callback=list', dataType:'jsonp', jsonpCallBack:'list' }) } </script> </body> </html>
CORS(客户端不变,服务端设置响应头)
随着技术的发展,现在的浏览器可以支持主动设置从而允许跨域请求,即:跨域资源共享(CORS,Cross-Origin Resource Sharing),其本质是设置响应头,使得浏览器允许跨域请求。
* 简单请求 OR 非简单请求
条件: 1、请求方式:HEAD、GET、POST 2、请求头信息: Accept Accept-Language Content-Language Last-Event-ID Content-Type 对应的值是以下三个中的任意一个 application/x-www-form-urlencoded multipart/form-data text/plain 注意:同时满足以上两个条件时,则是简单请求,否则为复杂请求 * 简单请求和非简单请求的区别? 简单请求:一次请求 非简单请求:两次请求,在发送数据之前会先发一次请求用于做“预检”,只有“预检”通过后才再发送一次请求用于数据传输。 * 关于“预检” - 请求方式:OPTIONS - “预检”其实做检查,检查如果通过则允许传输数据,检查不通过则不再发送真正想要发送的消息 - 如何“预检” => 如果复杂请求是PUT等请求,则服务端需要设置允许某请求,否则“预检”不通过 Access-Control-Request-Method => 如果复杂请求设置了请求头,则服务端需要设置允许某请求头,否则“预检”不通过 Access-Control-Request-Headers
a、支持跨域,简单请求(在服务端加响应头,带相应头就能过来)
服务器设置响应头:Access-Control-Allow-Origin = '域名' 或 '*' *表示所有的域名都可以访问
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <p> <input type="submit" onclick="XmlSendRequest();" /> </p> <p> <input type="submit" onclick="JqSendRequest();" /> </p> <script type="text/javascript" src="jquery-1.12.4.js"></script> <script> function XmlSendRequest(){ var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(){ if(xhr.readyState == 4) { var result = xhr.responseText; console.log(result); } }; xhr.open('GET', "http://c2.com:8000/test/", true); xhr.send(); } function JqSendRequest(){ $.ajax({ url: "http://c2.com:8000/test/", type: 'GET', dataType: 'text', success: function(data, statusText, xmlHttpRequest){ console.log(data); } }) } </script> </body> </html>
class MainHandler(tornado.web.RequestHandler): def get(self): self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com") self.write('{"status": true, "data": "seven"}')
b、支持跨域,复杂请求
由于复杂请求时,首先会发送“预检”请求,如果“预检”成功,则发送真实数据。
- “预检”请求时,允许请求方式则需服务器设置响应头:Access-Control-Request-Method
- “预检”请求时,允许请求头则需服务器设置响应头:Access-Control-Request-Headers
- “预检”缓存时间,服务器设置响应头:Access-Control-Max-Age
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <p> <input type="submit" onclick="XmlSendRequest();" /> </p> <p> <input type="submit" onclick="JqSendRequest();" /> </p> <script type="text/javascript" src="jquery-1.12.4.js"></script> <script> function XmlSendRequest(){ var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(){ if(xhr.readyState == 4) { var result = xhr.responseText; console.log(result); } }; xhr.open('PUT', "http://c2.com:8000/test/", true); xhr.setRequestHeader('k1', 'v1'); xhr.send(); } function JqSendRequest(){ $.ajax({ url: "http://c2.com:8000/test/", type: 'PUT', dataType: 'text', headers: {'k1': 'v1'}, success: function(data, statusText, xmlHttpRequest){ console.log(data); } }) } </script> </body> </html>
class MainHandler(tornado.web.RequestHandler): def put(self): self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com") self.write('{"status": true, "data": "seven"}') def options(self, *args, **kwargs): self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com") self.set_header('Access-Control-Allow-Headers', "k1,k2") self.set_header('Access-Control-Allow-Methods', "PUT,DELETE") self.set_header('Access-Control-Max-Age', 10)
八、上传文件
form表单上传文件的时候一定要记得加上 enctype="multipart/form-data"
#!/usr/bin/env/python # -*- coding:utf-8 -*- import tornado.web IMG_LIST = [] # class IndexHandler(tornado.web.RequestHandler): # def get(self): # print('asdas') # self.render('index.html') # def post(self, *args, **kwargs): # self.write('{"status":1,"message":"mmm"}') class PicturesHandler(tornado.web.RequestHandler): def get(self, *args, **kwargs): self.render('pictures.html',img_list = IMG_LIST) # self.render('ajaxsc.html',img_list = IMG_LIST) # self.render('ifname.html',img_list = IMG_LIST) # self.render('iframe.html',img_list = IMG_LIST) # def post(self, *args, **kwargs): print(self.get_argument('user')) print(self.get_arguments('favor')) file_maetas = self.request.files['hahaha'] for meta in file_maetas: file_name = meta['filename'] import os with open(os.path.join('static','img',file_name),'wb')as up: up.write(meta["body"]) IMG_LIST.append(file_name) self.write('{"status":1,"message":"mmm"}') settings = { 'template_path':"views", 'static_path':'static', } application = tornado.web.Application([ (r'/pictures',PicturesHandler) # (r'/ajaxsc', PicturesHandler) # (r'/ajaxjq', PicturesHandler) # (r'/ifname',PicturesHandler) # (r'/iframe',PicturesHandler) ],**settings) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <ul> {% for item in img_list %} <li><img style="width: 200px;height: 200px" src="/static/img/{{item}}"></li> {% end %} </ul> <form action="/pictures" method="post" enctype="multipart/form-data"> <input type="text" name = "user"/> <h1>性格类型</h1> <input type="checkbox" name="favor" value="1"/>暴虐的; <input type="checkbox" name="favor" value="2"/>温和的; <input type="checkbox" name="favor" value="3"/>傻二的; <input type="file" name="hahaha"/> <input type="submit" value="提交"/> </form> <script src="/static/jquery-1.12.4.js"></script> <script> function UploadFile(){ var fileobj = document.getElementById('img').files[0]; var form = new FormData(); form.append('user','uuu'); form.append('favor','1'); form.append('hahaha','fileobj'); var xhr = new XMLHttpRequest(); xhr.open('post','/pictures',true); xhr.send(form); } </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <input type="file" id="img" /> <input type="button" onclick="UploadFile();" value="提交按钮"/> <script src="/static/jquery-1.12.4.js"></script> <script> function UploadFile(){ var fileobj = document.getElementById('img').files[0]; var form = new FormData(); form.append('user','uuu'); form.append('favor','1'); form.append('hahaha',fileobj); var xhr = new XMLHttpRequest(); xhr.open('post','/ajaxsc',true); xhr.send(form); } </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <input type="file" id="img" /> <input type="button" onclick="UploadFile();" value="提交" /> <script src="/static/jquery-1.12.4.js"></script> <script> function UploadFile(){ var fileObj = $("#img")[0].files[0]; var form = new FormData(); form.append("user", "v1"); form.append('favor','1'); form.append("hahaha", fileObj); $.ajax({ type:'POST', url: '/ajaxjq', data: form, processData: false, // tell jQuery not to process the data contentType: false, // tell jQuery not to set contentType success: function(arg){ console.log(arg); } }) } </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .hide{ display: none; } </style> </head> <body> <form id="my_form" name="form" action="/ifname" method="POST" enctype="multipart/form-data" > <div id="main"> <input name="user" type="text" /> <input name="davor" type="text" /> <input name="hahaha" id="my_file" type="file" /> <input type="button" name="action" value="提交" onclick="redirect()"/> <iframe id='my_iframe' name='my_iframe' src="" class="hide"></iframe> </div> </form> <script src="/static/jquery-1.12.4.js"></script> <script> function redirect(){ document.getElementById('my_iframe').onload = Testt; //找到id为my_iframe 设置 onload 加载完成执行Testt函数 document.getElementById('my_form').target = 'my_iframe'; //将my_form 目标提交到 id为my_iframe document.getElementById('my_form').submit(); } function Testt(ths){ var t = $("#my_iframe").contents().find("body").text(); console.log(t); } </script> </body> </html>
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <input type="file" id="img" /> <input type="button" onclick="UploadFile();" /> <script> function UploadFile(){ var fileObj = document.getElementById("img").files[0]; var form = new FormData(); form.append("k1", "v1"); form.append("fff", fileObj); var xhr = new XMLHttpRequest(); xhr.open("post", '/index', true); xhr.send(form); } </script> </body> </html>
<script type="text/javascript"> $(document).ready(function () { $("#formsubmit").click(function () { var iframe = $('<iframe name="postiframe" id="postiframe" style="display: none"></iframe>'); $("body").append(iframe); var form = $('#theuploadform'); form.attr("action", "/upload.aspx"); form.attr("method", "post"); form.attr("encoding", "multipart/form-data"); form.attr("enctype", "multipart/form-data"); form.attr("target", "postiframe"); form.attr("file", $('#userfile').val()); form.submit(); $("#postiframe").load(function () { iframeContents = this.contentWindow.document.body.innerHTML; $("#textarea").html(iframeContents); }); return false; }); }); </script> <form id="theuploadform"> <input id="userfile" name="userfile" size="50" type="file" /> <input id="formsubmit" type="submit" value="Send File" /> </form> <div id="textarea"> </div>