概述:
由于Ueditor官方的后端只有PHP, ASP, ASP.NET, JSP四种,没有相应的python实现。我结合python的tornado框架实现了ueditor中的图片,文件及涂鸭上传的功能。
一. 下载Ueditor
从Ueditor官网下载最新版本: http://ueditor.baidu.com/build/build_down.php?n=ueditor&v=1_4_3_3-utf8-php
(这里我用的是php的版本)
二. 集成到tornado中
2.1 解压放置ueditor到项目中
将ueditor1_4_3_3-utf8-php.zip解压到项目目录 /static 下, 项目目录结构如下:
2.2 index.html
Ueditor
2.3 修改Ueditor的配置文件ueditor.config.js
window.UEDITOR_HOME_URL = "/static/ueditor/" // 添加ueditor在项目中的相对路径
, serverUrl: "/upload" // 修改服务器统一请求接口路径
2.4 添加后端部署文件config.json
"imageActionName": "uploadimage",
"imageFieldName": "upfile",
"imageMaxSize": 2048000,
"imageAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"],
"imageCompressEnable": true,
"imageCompressBorder": 1600,
"imageInsertAlign": "none",
"imageUrlPrefix": "http://127.0.0.1:5000/",
"imagePathFormat": "static/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}",
"scrawlActionName": "uploadscrawl",
"scrawlFieldName": "upfile",
"scrawlPathFormat": "static/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}",
"scrawlMaxSize": 2048000,
"scrawlUrlPrefix": "http://127.0.0.1:5000/",
"scrawlInsertAlign": "none",
"snapscreenActionName": "uploadimage",
"snapscreenPathFormat": "static/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}",
"snapscreenUrlPrefix": "http://127.0.0.1:5000/",
"snapscreenInsertAlign": "none",
"catcherLocalDomain": ["127.0.0.1", "localhost", "img.baidu.com"],
"catcherActionName": "catchimage",
"catcherFieldName": "source",
"catcherPathFormat": "static/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}",
"catcherUrlPrefix": "http://127.0.0.1:5000/",
"catcherMaxSize": 2048000,
"catcherAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"],
"videoActionName": "uploadvideo",
"videoFieldName": "upfile",
"videoPathFormat": "static/upload/video/{yyyy}{mm}{dd}/{time}{rand:6}",
"videoUrlPrefix": "http://127.0.0.1:5000/",
"videoMaxSize": 102400000,
"videoAllowFiles": [
".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg",
".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid"],
"fileActionName": "uploadfile",
"fileFieldName": "upfile",
"filePathFormat": "static/upload/file/{yyyy}{mm}{dd}/{time}{rand:6}",
"fileUrlPrefix": "http://127.0.0.1:5000/",
"fileMaxSize": 51200000,
"fileAllowFiles": [
".png", ".jpg", ".jpeg", ".gif", ".bmp",
".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg",
".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid",
".rar", ".zip", ".tar", ".gz", ".7z", ".bz2", ".cab", ".iso",
".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".md", ".xml"
],
"imageManagerActionName": "listimage",
"imageManagerListPath": "static/upload/image/",
"imageManagerListSize": 20,
"imageManagerUrlPrefix": "http://127.0.0.1:5000/",
"imageManagerInsertAlign": "none",
"imageManagerAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"],
"fileManagerActionName": "listfile",
"fileManagerListPath": "static/upload/file/",
"fileManagerUrlPrefix": "http://127.0.0.1:5000/",
"fileManagerListSize": 20,
"fileManagerAllowFiles": [
".png", ".jpg", ".jpeg", ".gif", ".bmp",
".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg",
".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid",
".rar", ".zip", ".tar", ".gz", ".7z", ".bz2", ".cab", ".iso",
".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".md", ".xml"
]
}
此文件由php目录中的config.json改写而成,但是需要注意: *PathFormat的static前不能有'/', 在windows进行路径连接时会出现问题。
2.5 tornado实现
myapp.py代码:
import os
import stat
from datetime import datetime
import random
import json
import base64
import tornado.httpserver
import tornado.ioloop
from tornado.web import Application, RequestHandler, authenticated
from tornado.options import define, options
define("port", default=5000, help="port", type=int)
basedir = os.path.dirname(os.path.abspath(__file__))
class Upload(object):
''' upload image or file '''
def __init__(self):
self.config = {}
self.oriName = '' # 原始文件名
self.fileName = '' # 新文件名
self.fullName = '' # 完整文件名,即从当前配置目录开始的URL
self.filePath = '' # 完整文件名,即从当前配置目录开始的URL
self.fileSize = 0 # 文件大小
self.fileType = '' # 文件类型
self.stateMap = {
"SUCCESS": "SUCCESS", # 上传成功标记,在UEditor中内不可改变,否则flash判断会出错
"ERROR_FILE_MAXSIZE": "文件大小超出 upload_max_filesize 限制",
"ERROR_FILE_LIMITSIZE": "文件大小超出 MAX_FILE_SIZE 限制",
"ERROR_FILE_UPLOAD_FAILED": "文件未被完整上传",
"ERROR_FILE_NOT_UPLOAD": "没有文件被上传",
"ERROR_FILE_NULL": "上传文件为空",
"ERROR_SIZE_EXCEED": "文件大小超出网站限制",
"ERROR_TYPE_NOT_ALLOWED": "文件类型不允许",
"ERROR_CREATE_DIR": "目录创建失败",
"ERROR_DIR_NOT_WRITEABLE": "目录没有写权限",
"ERROR_FILE_SAVE": "文件保存时出错",
"ERROR_FILE_NOT_FOUND": "找不到上传文件",
"ERROR_WRITE_CONTENT": "写入文件内容错误"
}
def getItem(self, key):
fp = open("static/ueditor/config.json", 'r')
config = json.loads(fp.read())
fp.close()
for k, v in config.items():
if k == key:
return v
def getStateInfo(self, stateinfo):
for k, v in self.stateMap.items():
if k == stateinfo:
return v
def checkSize(self):
if self.fileSize > self.config['maxSize']:
return False
else:
return True
def checkType(self):
if self.fileType in self.config['allowFiles']:
return True
else:
return False
def getFullName(self):
now = datetime.now()
randint = random.randint(100000, 999999)
format = self.config['pathFormat']
format = format.replace("{yyyy}", now.strftime("%Y"))
format = format.replace("{mm}", now.strftime("%m"))
format = format.replace("{dd}", now.strftime("%d"))
format = format.replace("{time}", now.strftime("%H%M%S"))
format = format.replace("{rand:6}", str(randint))
ext = self.oriName[self.oriName.rfind("."):]
self.fileName = "%s%s%s" % (now.strftime("%H%M%S"), randint, ext)
return format + ext
def getFilePath(self):
fullpath = os.path.join(basedir, self.fullName)
return fullpath
def uploadFile(self, upfile):
result = {'state': '', 'url': '', 'title': '', 'original': ''}
if not upfile or len(upfile) == 0:
result['state'] = self.getStateInfo('ERROR_FILE_NOT_UPLOAD')
return result
self.oriName = upfile[0]['filename']
self.fileType = self.oriName[self.oriName.rfind('.'):]
data = upfile[0]['body']
self.fileSize = len(data)
if self.fileSize == 0:
result['state'] = self.getStateInfo('ERROR_FILE_NULL')
return result
if not self.checkSize():
result['state'] = self.getStateInfo('ERROR_SIZE_EXCEED')
return result
if not self.checkType():
result['state'] = self.getStateInfo('ERROR_TYPE_NOT_ALLOWED')
return result
self.fullName = self.getFullName()
self.filePath = self.getFilePath()
dirname = os.path.dirname(self.filePath)
if not os.path.exists(dirname):
try:
os.makedirs(dirname)
except Exception as e:
result['state'] = self.getStateInfo('ERROR_CREATE_DIR')
return result
if not os.access(dirname, os.R_OK | os.W_OK):
try:
os.chmod(dirname, stat.S_IREAD | stat.S_IWRITE)
except Exception as e:
result['state'] = self.getStateInfo('ERROR_DIR_NOT_WRITEABLE')
return result
try:
fp = open(self.filePath, 'wb')
fp.write(data)
fp.close()
except Exception as e:
result['state'] = self.getStateInfo('ERROR_FILE_SAVE')
return result
result['state'] = self.stateMap['SUCCESS']
result['url'] = self.fullName
result['title'] = self.fileName
result['original'] = self.oriName
return result
def getFileList(self, start, size):
result = {'state': '', 'list': [], 'start': 0, 'total': 0}
path = self.config['path']
listSize = self.config['listSize']
listfiles = []
for root, dirs, files in os.walk(path):
for file in files:
self.fileType = file[file.rfind('.'):]
if self.checkType():
url = root + '/' + file
listfiles.append({'url': '%s' % url})
if size > listSize:
num = listSize
else:
num = size
listfiles.sort()
lists = listfiles[start:(start + num)]
result['state'] = self.stateMap['SUCCESS']
result['list'] = lists
result['start'] = start
result['total'] = len(listfiles)
return result
class UploadHandler(RequestHandler):
def get(self):
action = self.get_argument('action')
upload = Upload()
result = {}
if action == "config":
fp = open("static/ueditor/config.json", 'r')
config = json.loads(fp.read())
fp.close()
result = config
if action == "listimage":
start = self.get_argument('start')
size = self.get_argument('size')
upload.config['path'] = upload.getItem('imageManagerListPath')
upload.config['listSize'] = upload.getItem('imageManagerListSize')
upload.config['allowFiles'] = upload.getItem('imageManagerAllowFiles')
result = upload.getFileList(int(start), int(size))
if action == "listfile":
start = self.get_argument('start')
size = self.get_argument('size')
upload.config['path'] = upload.getItem('fileManagerListPath')
upload.config['listSize'] = upload.getItem('fileManagerListSize')
upload.config['allowFiles'] = upload.getItem('fileManagerAllowFiles')
result = upload.getFileList(int(start), int(size))
self.write(result)
def post(self):
action = self.get_argument('action')
upload = Upload()
result = {}
if action == "uploadimage":
fieldName = upload.getItem('imageFieldName')
upload.config['pathFormat'] = upload.getItem('imagePathFormat')
upload.config['maxSize'] = upload.getItem('imageMaxSize')
upload.config['allowFiles'] = upload.getItem('imageAllowFiles')
upfile = self.request.files[fieldName]
result = upload.uploadFile(upfile)
if action == "uploadfile":
fieldName = upload.getItem('fileFieldName')
upload.config['pathFormat'] = upload.getItem('filePathFormat')
upload.config['maxSize'] = upload.getItem('fileMaxSize')
upload.config['allowFiles'] = upload.getItem('fileAllowFiles')
upfile = self.request.files[fieldName]
result = upload.uploadFile(upfile)
if action == "uploadvideo":
fieldName = upload.getItem('videoFieldName')
upload.config['pathFormat'] = upload.getItem('videoPathFormat')
upload.config['maxSize'] = upload.getItem('videoMaxSize')
upload.config['allowFiles'] = upload.getItem('videoAllowFiles')
upfile = self.request.files[fieldName]
result = upload.uploadFile(upfile)
if action == "uploadscrawl":
upload.config['pathFormat'] = upload.getItem('scrawlPathFormat')
upload.config['maxSize'] = upload.getItem('scrawlMaxSize')
upload.config['allowFiles'] = ['.png']
data = self.request.body_arguments
upfile = []
upfile.append({'filename': 'scrawl.png', 'body': base64.b64decode(data['upfile'][0])})
result = upload.uploadFile(upfile)
self.write(result)
def main():
settings = {
"autoreload": True,
"debug": True,
"static_path": os.path.join(basedir, "static"),
"template_path": os.path.join(basedir, "templates")
}
app = Application(
[
(r"/", IndexHandler),
(r"/upload", UploadHandler)
],
**settings
)
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.current().start()
if __name__ == '__main__':
main()