Tornado使用Ueditor的一些操作
来源:互联网 发布:软件蓝图设计 编辑:程序博客网 时间:2024/06/06 06:34
- Tornado使用Ueditor的一些操作
- Tornado简介
- UEditor简介
- 在Tornado项目中集成UEditor
- 后台请求的实现
- 验证
- 参考文献
Tornado使用Ueditor的一些操作
本文介绍如何使用Tornado框架来集成百度的富文本编辑框架Ueditor:
感谢 problc 、 digwtx 大神的博客指导
Tornado简介
Tornado是一个用Python写的相对简单的、不设障碍的Web服务器架构,用以处理上万的同时的连接口,让实时的Web服务通畅起来。虽然跟现在的一些用Python写的Web架构相似,比如Django,但Tornado更注重速度,能够处理海量的同时发生的流量
文档链接:http://tornado-zh.readthedocs.io/zh/latest/
UEditor简介
UEditor是由百度WEB前端研发部开发的所见即所得的开源富文本编辑器,具有轻量、可定制、用户体验优秀等特点。开源基于BSD协议,所有源代码在协议允许范围内可自由修改和使用。百度UEditor的推出,可以帮助不少网站开发者在开发富文本编辑器所遇到的难题,节约开发者因开发富文本编辑器所需要的大量时间,有效降低了企业的开发成本。 —— [ 百度百科 ]
在Tornado项目中集成UEditor
首先下载UEditor,解压之后可以查看Ueditor的目录结构:
通过这个结构我们发现,Ueditor支持PHP、Jsp、Asp、.net 版本,但是目前还没有支持Python版本,网上已经有人更改了一些python版本,比如flask集成Ueditor(传送门)、web.py集成Ueditor(传送门)等等,但是目前还没有找到关于Tornado集成Ueditor的版本,所以本人结合前边大神的成果,略微做了一下更改,改成了Tornado版本,希望可以帮到大家。
- 在项目中加入UEditor
在teplates文件夹下的任意html(你需要使用到富文本的页面)模板文件中引入Ueditor
<!--在header配置引入js文件:--><script type="text/javascript" src="{{ static_url('ueditor/1.4.3/ueditor.config.js') }}"></script><script type="text/javascript" src="{{ static_url('ueditor/1.4.3/ueditor.all.min.js') }}"> </script><script type="text/javascript" src="{{ static_url('ueditor/1.4.3/lang/zh-cn/zh-cn.js') }}"></script>
<!--在body加入UEditor:--><script id="editor" name="editor" type="text/plain" style="width:100%;height:400px;"></script>
配置请求路径
UEditor 推荐使用统一的请求路径,在部署好前端代码后,需要修改
ueditor.config.js
里的 serverUrl 参数(或者初始化时指定),改成 ‘/ueditor/upload/’ (这个请求参数是你自己的请求路径,可以是任意的,不需要固定)。
详细配置、请求格式内容参见官方文档
后台请求的实现
编写/ueditor/upload所对应的Python代码RemoteRequestHanler.py
# -*- coding: utf-8 -*-#代码是仿照Flask集成UEditor的大神 digwtx 写的代码改的import osimport reimport jsonimport tornado.webfrom conf.settings import static_pathfrom tools.uploader import Uploaderclass RemotePictureHandler(tornado.web.RequestHandler): def post(self, *args, **kwargs): self.upload(args,kwargs) def get(self, *args, **kwargs): self.upload(args,kwargs) def options(self, *args, **kwargs): self.upload(args,kwargs) def upload(self,*args, **kwargs): """UEditor文件上传接口 config 配置文件 result 返回结果 """ mimetype = 'application/json' result = {} action = self.get_argument('action') # 解析JSON格式的配置文件 with open(os.path.join(static_path, 'Widget','ueditor','1.4.3', 'php', 'config.json')) as fp: try: # 删除 `/**/` 之间的注释 CONFIG = json.loads(re.sub(r'\/\*.*\*\/', '', fp.read())) except: CONFIG = {} if action == 'config': # 初始化时,返回配置文件给客户端 result = CONFIG elif action in ('uploadimage', 'uploadfile', 'uploadvideo'): # 图片、文件、视频上传 if action == 'uploadimage': fieldName = CONFIG.get('imageFieldName') config = { "pathFormat": CONFIG['imagePathFormat'], "maxSize": CONFIG['imageMaxSize'], "allowFiles": CONFIG['imageAllowFiles'] } elif action == 'uploadvideo': fieldName = CONFIG.get('videoFieldName') config = { "pathFormat": CONFIG['videoPathFormat'], "maxSize": CONFIG['videoMaxSize'], "allowFiles": CONFIG['videoAllowFiles'] } else: fieldName = CONFIG.get('fileFieldName') config = { "pathFormat": CONFIG['filePathFormat'], "maxSize": CONFIG['fileMaxSize'], "allowFiles": CONFIG['fileAllowFiles'] } if fieldName in self.request.files: field = self.request.files[fieldName] for fieldsss in field: uploader = Uploader(fieldsss, config, static_path) result = uploader.getFileInfo() break else: result['state'] = '上传接口出错' elif action in ('uploadscrawl'): # 涂鸦上传 fieldName = CONFIG.get('scrawlFieldName') config = { "pathFormat": CONFIG.get('scrawlPathFormat'), "maxSize": CONFIG.get('scrawlMaxSize'), "allowFiles": CONFIG.get('scrawlAllowFiles'), "oriName": "scrawl.png" } if fieldName in self.request.form: field = self.request.form[fieldName] uploader = Uploader(field, config, static_path, 'base64') result = uploader.getFileInfo() else: result['state'] = '上传接口出错' elif action in ('catchimage'): config = { "pathFormat": CONFIG['catcherPathFormat'], "maxSize": CONFIG['catcherMaxSize'], "allowFiles": CONFIG['catcherAllowFiles'], "oriName": "remote.png" } fieldName = CONFIG['catcherFieldName'] if fieldName in self.request.form: # 这里比较奇怪,远程抓图提交的表单名称不是这个 source = [] elif '%s[]' % fieldName in self.request.form: # 而是这个 source = self.request.form.getlist('%s[]' % fieldName) _list = [] for imgurl in source: uploader = Uploader(imgurl, config, static_path, 'remote') info = uploader.getFileInfo() _list.append({ 'state': info['state'], 'url': info['url'], 'original': info['original'], 'source': imgurl, }) result['state'] = 'SUCCESS' if len(_list) > 0 else 'ERROR' result['list'] = _list else: result['state'] = '请求地址出错' result = json.dumps(result) if self.request.arguments.has_key("callback"): callback = self.get_argument('callback') if re.match(r'^[\w_]+$', callback): result = '%s(%s)' % (callback, result) mimetype = 'application/javascript' else: result = json.dumps({'state': 'callback参数不合法'}) self.set_header('Content-Type', mimetype) self.set_header('Access-Control-Allow-Origin','*') self.set_header('Access-Control-Allow-Headers', 'X-Requested-With,X_Requested_With') self.write(result) self.finish()
编写保存上传文件的工具类uploader.py:
# -*- coding: utf-8 -*-import osimport reimport jsonimport base64import randomimport urllibimport datetimefrom werkzeug.utils import secure_filenameclass Uploader: stateMap = [ # 上传状态映射表,国际化用户需考虑此处数据的国际化 "SUCCESS", # 上传成功标记,在UEditor中内不可改变,否则flash判断会出错 "文件大小超出 upload_max_filesize 限制", "文件大小超出 MAX_FILE_SIZE 限制", "文件未被完整上传", "没有文件被上传", "上传文件为空", ] stateError = { "ERROR_TMP_FILE": "临时文件错误", "ERROR_TMP_FILE_NOT_FOUND": "找不到临时文件", "ERROR_SIZE_EXCEED": "文件大小超出网站限制", "ERROR_TYPE_NOT_ALLOWED": "文件类型不允许", "ERROR_CREATE_DIR": "目录创建失败", "ERROR_DIR_NOT_WRITEABLE": "目录没有写权限", "ERROR_FILE_MOVE": "文件保存时出错", "ERROR_FILE_NOT_FOUND": "找不到上传文件", "ERROR_WRITE_CONTENT": "写入文件内容错误", "ERROR_UNKNOWN": "未知错误", "ERROR_DEAD_LINK": "链接不可用", "ERROR_HTTP_LINK": "链接不是http链接", "ERROR_HTTP_CONTENTTYPE": "链接contentType不正确" } def __init__(self, fileobj, config, static_folder, _type=None): """ :param fileobj: FileStorage, Base64Encode Data or Image URL :param config: 配置信息 :param static_folder: 文件保存的目录 :param _type: 上传动作的类型,base64,remote,其它 """ self.fileobj = fileobj self.config = config self.static_folder = static_folder self._type = _type if _type == 'base64': self.upBase64() elif _type == 'remote': self.saveRemote() else: self.upFile() def upBase64(self): # 处理base64编码的图片上传 img = base64.b64decode(self.fileobj) self.oriName = self.config['oriName'] self.fileSize = len(img) self.fileType = self.getFileExt() self.fullName = self.getFullName() self.filePath = self.getFilePath() # 检查文件大小是否超出限制 if not self.checkSize(): self.stateInfo = self.getStateError('ERROR_SIZE_EXCEED') return # 检查路径是否存在,不存在则创建 dirname = os.path.dirname(self.filePath) if not os.path.exists(dirname): try: os.makedirs(dirname) except: self.stateInfo = self.getStateError('ERROR_CREATE_DIR') return elif not os.access(dirname, os.W_OK): self.stateInfo = self.getStateError('ERROR_DIR_NOT_WRITEABLE') return try: with open(self.filePath, 'wb') as fp: fp.write(img) self.stateInfo = self.stateMap[0] except: self.stateInfo = self.getStateError('ERROR_FILE_MOVE') return def upFile(self): # 上传文件的主处理方法 self.oriName = self.fileobj['filename'] # 获取文件大小 self.fileSize = len(self.fileobj['body']) self.fileType = self.getFileExt() self.fullName = self.getFullName() self.filePath = self.getFilePath() # 检查文件大小是否超出限制 if not self.checkSize(): self.stateInfo = self.getStateError('ERROR_SIZE_EXCEED') return # 检查是否不允许的文件格式 if not self.checkType(): self.stateInfo = self.getStateError('ERROR_TYPE_NOT_ALLOWED') return # 检查路径是否存在,不存在则创建 dirname = os.path.dirname(self.filePath) if not os.path.exists(dirname): try: os.makedirs(dirname) except: self.stateInfo = self.getStateError('ERROR_CREATE_DIR') return elif not os.access(dirname, os.W_OK): self.stateInfo = self.getStateError('ERROR_DIR_NOT_WRITEABLE') return # 保存文件 try: with open(self.filePath, 'wb') as up: # 有些文件需要已二进制的形式存储,实际中可以更改 up.write(self.fileobj['body']) self.stateInfo = self.stateMap[0] except: self.stateInfo = self.getStateError('ERROR_FILE_MOVE') return def saveRemote(self): _file = urllib.urlopen(self.fileobj) self.oriName = self.config['oriName'] self.fileSize = 0 self.fileType = self.getFileExt() self.fullName = self.getFullName() self.filePath = self.getFilePath() # 检查文件大小是否超出限制 if not self.checkSize(): self.stateInfo = self.getStateError('ERROR_SIZE_EXCEED') return # 检查路径是否存在,不存在则创建 dirname = os.path.dirname(self.filePath) if not os.path.exists(dirname): try: os.makedirs(dirname) except: self.stateInfo = self.getStateError('ERROR_CREATE_DIR') return elif not os.access(dirname, os.W_OK): self.stateInfo = self.getStateError('ERROR_DIR_NOT_WRITEABLE') return try: with open(self.filePath, 'wb') as fp: fp.write(_file.read()) self.stateInfo = self.stateMap[0] except: self.stateInfo = self.getStateError('ERROR_FILE_MOVE') return def getStateError(self, error): # 上传错误检查 return self.stateError.get(error, 'ERROR_UNKNOWN') def checkSize(self): # 文件大小检测 return self.fileSize <= self.config['maxSize'] def checkType(self): # 文件类型检测 return self.fileType.lower() in self.config['allowFiles'] def getFilePath(self): # 获取文件完整路径 rootPath = self.static_folder filePath = '' for path in self.fullName.split('/'): filePath = os.path.join(filePath, path) return os.path.join(rootPath, filePath) def getFileExt(self): # 获取文件扩展名 return ('.%s' % self.oriName.split('.')[-1]).lower() def getFullName(self): # 重命名文件 now = datetime.datetime.now() _time = now.strftime('%H%M%S') # 替换日期事件 _format = self.config['pathFormat'] _format = _format.replace('{yyyy}', str(now.year)) _format = _format.replace('{mm}', str(now.month)) _format = _format.replace('{dd}', str(now.day)) _format = _format.replace('{hh}', str(now.hour)) _format = _format.replace('{ii}', str(now.minute)) _format = _format.replace('{ss}', str(now.second)) _format = _format.replace('{ss}', str(now.second)) _format = _format.replace('{time}', _time) # 过滤文件名的非法自负,并替换文件名 _format = _format.replace('{filename}', secure_filename(self.oriName)) # 替换随机字符串 rand_re = r'\{rand\:(\d*)\}' _pattern = re.compile(rand_re, flags=re.I) _match = _pattern.search(_format) if _match is not None: n = int(_match.groups()[0]) _format = _pattern.sub(str(random.randrange(10**(n-1), 10**n)), _format) _ext = self.getFileExt() return '%s%s' % (_format, _ext) def getFileInfo(self): # 获取当前上传成功文件的各项信息 filename = re.sub(r'^/', '', self.fullName) return { 'state': self.stateInfo, 'url': os.path.join('http://127.0.0.1:8800','static',filename), 'title': self.oriName, 'original': self.oriName, 'type': self.fileType, 'size': self.fileSize, }
配置请求url
SETTINGS = { "template_path": os.path.join(os.path.dirname(os.path.dirname(__file__)), "templates"), "static_path": os.path.join(os.path.dirname(os.path.dirname(__file__)), "static"), 'debug': True,}urls=[ (r"/index", IndexHandler), (r"/ueditor/upload/",RemotePictureHandler)]app = tornado.web.Application( handlers=urls, **SETTINGS)
验证
输入你的url找到跳转到前端的页面:
然后点击选择图片上传,就可以看到上传的结果
结果保存在你的应用目录下:
OK!大功告成!!!!
参考文献:
1.Flask项目集成富文本编辑器UEditor 实现图片上传功能:http://flask123.sinaapp.com/article/47/
2.在python web.py中使用百度富文本编辑器 UEditor :http://blog.csdn.net/problc/article/details/19155007
- Tornado使用Ueditor的一些操作
- UEditor编辑器的一些使用心得
- asp.net中对UEditor编辑器的一些操作
- ueditor的一些设置
- tornado的简单使用
- tornado的mysql数据库操作
- tornado框架SQLAlchemy的操作
- UEditor如何使用?UEditor的配置教程
- 百度UEditor的使用
- 百度UEditor的使用
- ueditor的使用
- UEditor的使用
- ueditor的使用
- 使用Baidu的Ueditor
- ueditor的使用
- Ueditor编辑器的使用
- UEditor的使用
- UEditor编辑器的使用
- Hyperledger Fabric v1.0 部署指南
- 登录
- mysql远程连接访问
- 图的邻接表示法Java版
- JUST SO SO之 MySQL
- Tornado使用Ueditor的一些操作
- Python随笔1-练习题
- Jquery如何获取select选中项 自定义属性的值?
- hdu 4565 (快速矩阵幂)
- ARP协议
- nyoj37 回文字符串
- RocketMQ最佳实践(二)4.0版本/集群
- 2017-04-12 DBA日记,频繁commit导致的log file sync的诊断
- Python+Selenium练习篇之30-获取当前页面全部图片信息