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目录结构
通过这个结构我们发现,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找到跳转到前端的页面:

UEditor案例

然后点击选择图片上传,就可以看到上传的结果

这里写图片描述

结果保存在你的应用目录下:
这里写图片描述

OK!大功告成!!!!

参考文献:

1.Flask项目集成富文本编辑器UEditor 实现图片上传功能:http://flask123.sinaapp.com/article/47/
2.在python web.py中使用百度富文本编辑器 UEditor :http://blog.csdn.net/problc/article/details/19155007

0 0
原创粉丝点击