赞
踩
从 官网下载UEditor源码,下载的源码中并没有ueditor.all.js文件。需要使用grunt来把源码包打包成部署版本(含有ueditor.all.js 文件)
把UEditor部署版本放到项目的static静态目录下,然后按照官网的说明在相关要使用的页面引入ueditor.config.js和ueditor.all.js文件,就可以使用UEditor的绝大部分功能。只有上传文件、图片、视频、在线图片、在线文件这一部分和上传有关系的部分不好用,因为这些功能需要后端服务器的支持才可以,所以需要在我们的django项目中实现响应的视图函数来处理这部分请求:
上传图片、上传截图、上传视频、上传附件和上传涂鸦图片,后台服务器应返回的JSON格式数据如下:
{
"state": "SUCCESS", // 状态信息,成功时返回值固定为SUCCESS
"url": "/ueditor/2_%E5%B9%BF%E5%91%8A%E6%8E%92%E6%9C%9F%E5%B9%B3%E5%8F%B0/%E6%9C%80%E6%96%B0%E7%89%A9%E6%96%99%E5%8D%95ID%E6%98%BE%E7%A4%BA%E9%94%99%E8%AF%AF001.png",
"title": "最新物料单ID显示错误001.png", // 文件名称
"original": "最新物料单ID显示错误001.png" // 内部文件名,一般和titile相同
}
抓取远程图片,后台服务器应返回的JSON格式数据如下:
{
"state": "SUCCESS",
"list": [
{
"state": "SUCCESS",
"url": "/ueditor/2_%E5%B9%BF%E5%91%8A%E6%8E%92%E6%9C%9F%E5%B9%B3%E5%8F%B0/sww.png",
"size": 7200,
"title": "sww.png",
"original": "sww.png",
"source": "http://seventest.cn/static/images/sww.png", // 下载地址
}
]
}
列出指定目录下的图片、列出指定目录下的文件,后台服务器应返回的JSON格式数据如下:
{ "state": "SUCCESS", "list": [ { "url": "/ueditor/2_%E5%B9%BF%E5%91%8A%E6%8E%92%E6%9C%9F%E5%B9%B3%E5%8F%B0/del_image_01.png" }, { "url": "/ueditor/2_%E5%B9%BF%E5%91%8A%E6%8E%92%E6%9C%9F%E5%B9%B3%E5%8F%B0/image.png" }, { "url": "/ueditor/2_%E5%B9%BF%E5%91%8A%E6%8E%92%E6%9C%9F%E5%B9%B3%E5%8F%B0/test_picture_002.gif" }, { "url": "/ueditor/2_%E5%B9%BF%E5%91%8A%E6%8E%92%E6%9C%9F%E5%B9%B3%E5%8F%B0/%E6%9C%80%E6%96%B0%E7%89%A9%E6%96%99%E5%8D%95ID%E6%98%BE%E7%A4%BA%E9%94%99%E8%AF%AF001.png" }, { "url": "/ueditor/2_%E5%B9%BF%E5%91%8A%E6%8E%92%E6%9C%9F%E5%B9%B3%E5%8F%B0/%E6%B6%82%E9%B8%A6.png" } ], "start": 0, "total": 5 }
以下是我开发的转测试流程管理系统中实现处理以上ueditor请求的相关代码
settings.py配置
项目路由配置(urls.py)
# -*- coding:utf-8 -*- ''' Created on 2019年12月15日 @author: siwenwei ''' from django.urls import re_path from . import views urlpatterns = [ re_path(r'^$', views.Index.as_view()), re_path(r'^login$', views.Login.as_view()), re_path(r'^logout$', views.Logout.as_view()), re_path(r'^userguide$', views.UserGuide.as_view()), re_path(r'^author$', views.Author.as_view()), re_path(r'^register$', views.Register.as_view()), re_path(r'^{}$'.format(views.Settings.suburl), views.Settings.as_view()), re_path(r'^{}$'.format(views.BaseData.suburl), views.BaseData.as_view()), re_path(r'^testproject/{}$'.format(views.Project.suburl), views.Project.as_view()), re_path(r'^user/list$', views.UserList.as_view()), re_path(r'^member/{}$'.format(views.Member.suburl), views.Member.as_view()), re_path(r'^flow/list$', views.FlowList.as_view()), re_path(r'^flow/{}$'.format(views.Flow.suburl), views.Flow.as_view()), re_path(r'^flow/node/list$', views.FlowNodes.as_view()), re_path(r'^flow/node/settings$', views.NodeSettings.as_view()), re_path(r'^flow/node/run$', views.NodeRunner.as_view()), re_path(r'^flow/node/file/{}$'.format(views.NodeFiles.suburl), views.NodeFiles.as_view()), re_path(r'^{}$'.format(views.UEditor.SERVER_URL['value']), views.UEditor.as_view()), # 处理 ueditor 获取服务端配置 上传图片 上传附件 列出所有图片 列出所有附件 删除图片 删除附件等请求 re_path(r'^{}/{}$'.format(views.UEditor.SERVER_URL['value'], views.Ueditor_FileServer.suburl), views.Ueditor_FileServer.as_view()), # 处理ueditor 获取各种文件的请求,其实就是提供获取静态文件的服务 ]
#!/usr/bin/env python # -*- encoding: utf-8 -*- ''' @Author: 思文伟 @Date: 2021/03/07 14:45:44 ''' import os import json import base64 from urllib.parse import quote from urllib.parse import unquote from urllib.request import urlopen from django.views import View from django.conf import settings from django.http import HttpResponse from django.http import JsonResponse from django.http import HttpResponseBadRequest from django.core.exceptions import ObjectDoesNotExist from django.utils.decorators import method_decorator from django.views.decorators.csrf import csrf_exempt from django.http import QueryDict from .. import models from ..utils.filesize import FileSize from ..utils.ueditor_path_formatter import UeditorPathFormatter @method_decorator(csrf_exempt, name='dispatch') class UEditor(View): ROOT_DIR = settings.UEDITOR_FILE_DIR SERVER_URL = {'key': 'serverUrl', 'value': 'ueditor', 'root_dir': ROOT_DIR} # 上传图片配置项 IMAGE = { "imageActionName": "uploadimage", # 执行上传图片的action名称 "imageMaxSize": 20485760, # 上传大小限制,单位B,10M "imageFieldName": "upfile", # * 提交的图片表单名称 "imageUrlPrefix": "", "imagePathFormat": "", "imageAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], # 上传图片格式显示 } # 涂鸦图片上传配置项 SCRAWL = { "scrawlActionName": "uploadscrawl", # 执行上传涂鸦的action名称 "scrawlFieldName": "upfile", # 提交的图片表单名称 "scrawlMaxSize": 10485760, # 上传大小限制,单位B 10M "scrawlUrlPrefix": "", "scrawlPathFormat": "", } # 截图工具上传 SNAPSCREEN = { "snapscreenActionName": "uploadimage", # 执行上传截图的action名称 "snapscreenPathFormat": "", "snapscreenUrlPrefix": "", } # 抓取远程图片配置 CATCHER = { "catcherLocalDomain": ["127.0.0.1", "localhost", "img.baidu.com"], "catcherPathFormat": "", "catcherActionName": "catchimage", # 执行抓取远程图片的action名称 "catcherFieldName": "source", # 提交的图片列表表单名称 "catcherMaxSize": 10485760, # 上传大小限制,单位B "catcherAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], # 抓取图片格式显示 "catcherUrlPrefix": "", } # 上传视频配置 VIDEO = { "videoActionName": "uploadvideo", # 执行上传视频的action名称 "videoPathFormat": "", "videoFieldName": "upfile", # 提交的视频表单名称 "videoMaxSize": 102400000, # 上传大小限制,单位B,默认100MB "videoUrlPrefix": "", "videoAllowFiles": [".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg", ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid"], # 上传视频格式显示 } # 上传文件配置 __FILE_ALLOW_FILES = [ ".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" ] FILE = { "fileActionName": "uploadfile", # controller里,执行上传视频的action名称 "filePathFormat": "", "fileFieldName": "upfile", # 提交的文件表单名称 "fileMaxSize": 204800000, # 上传大小限制,单位B,200MB "fileUrlPrefix": "", # 文件访问路径前缀 "fileAllowFiles": __FILE_ALLOW_FILES, # 上传文件格式显示 } # 列出指定目录下的图片 IMAGE_MANAGER = { "imageManagerActionName": "listimages", # 执行图片管理的action名称 "imageManagerListPath": "", "imageManagerListSize": 30, # 每次列出文件数量 # 列出的文件类型 "imageManagerAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], "imageManagerUrlPrefix": "", # 图片访问路径前缀 } # 列出指定目录下的文件 __FILE_MANAGER_ALLOW_FILES = [ ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".tif", ".psd" ".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", ".exe", ".com", ".dll", ".msi" ] FILE_MANAGER = { "fileManagerActionName": "listfiles", # 执行文件管理的action名称 "fileManagerListPath": "", "fileManagerUrlPrefix": "", "fileManagerListSize": 30, # 每次列出文件数量 "fileManagerAllowFiles": __FILE_MANAGER_ALLOW_FILES # 列出的文件类型 } DEFAULT_MAX_SIZE = 20485760 IMAGE_REMOVE = {"imageRemoveActionName": "removeimage"} FILE_REMOVE = {"fileRemoveActionName": "removefile"} SAVE_TO_SERVER = {"saveToServerActionName": "savecontent"} @property def upload_config(self): """返回给前端的上传配置项""" self.IMAGE.setdefault('imageActionName ', 'uploadimage') self.SCRAWL.setdefault("scrawlActionName", "uploadscrawl") self.SNAPSCREEN.setdefault("snapscreenActionName", "uploadimage") self.CATCHER.setdefault("catcherActionName", "catchimage") self.VIDEO.setdefault("videoActionName", "uploadvideo") self.FILE.setdefault("fileActionName", "uploadfile") self.IMAGE_MANAGER.setdefault("imageManagerActionName", "listimages") self.FILE_MANAGER.setdefault("fileManagerActionName", "listfiles") self.IMAGE_REMOVE.setdefault("imageRemoveActionName", "removeimage") self.FILE_REMOVE.setdefault("fileRemoveActionName", "removefile") self.SAVE_TO_SERVER.setdefault("saveToServerActionName", "savecontent") to_be_add_items = [self.IMAGE, self.SCRAWL, self.SNAPSCREEN, self.CATCHER] to_be_add_items.append(self.VIDEO) to_be_add_items.append(self.FILE) to_be_add_items.append(self.IMAGE_MANAGER) to_be_add_items.append(self.FILE_MANAGER) to_be_add_items.append(self.IMAGE_REMOVE) to_be_add_items.append(self.FILE_REMOVE) to_be_add_items.append(self.SAVE_TO_SERVER) items = {} for item in to_be_add_items: items.update(item) return items @property def action_views(self): """配置对应action的视图函数""" views = { 'config': self.get_ueditor_config, self.upload_config.get('imageActionName '): self.general_upload_file, self.upload_config.get("scrawlActionName"): self.upload_scrawl, self.upload_config.get("snapscreenActionName"): self.general_upload_file, self.upload_config.get("catcherActionName"): self.catch_remote_image, self.upload_config.get("videoActionName"): self.general_upload_file, self.upload_config.get("fileActionName"): self.general_upload_file, self.upload_config.get("imageManagerActionName"): self.list_images, self.upload_config.get("fileManagerActionName"): self.list_files, self.upload_config.get("imageRemoveActionName"): self.remove_file, self.upload_config.get("fileRemoveActionName"): self.remove_file, self.upload_config.get("saveToServerActionName"): self.save_ueditor_content, } return views @staticmethod def to_json(obj): return json.dumps(obj, ensure_ascii=False) @staticmethod def get_or_post(request): if request.method.upper() == 'get'.upper(): return request.GET elif request.method.upper() == 'post'.upper(): parts = request.get_full_path().split('?') query_params = parts[1] if len(parts) > 1 else '' qd = QueryDict(query_params) final_post = request.POST.copy() final_post.update(qd.copy()) return final_post else: return None def call_action_views(self, request, *args, **kwargs): params = self.get_or_post(request) action = params.get('action') action_view = self.action_views.get(action, None) if action_view: return action_view(request, *args, **kwargs) else: message = '没有为{}配置对应的视图函数'.format(action) return HttpResponseBadRequest(message) def create_full_file_path(self, subpath): """构建ueditor上传存放目录 Args: subpath: 子路径 """ return os.path.join(self.ROOT_DIR, subpath) def get_ueditor_config(self, request, *args, **kwargs): """返回配置""" return HttpResponse(self.to_json(self.upload_config), content_type="application/javascript") def get(self, request, *args, **kwargs): """获取ueditor的后端URL地址""" return self.call_action_views(request, *args, **kwargs) def post(self, request, *args, **kwargs): """获取ueditor的后端URL地址""" return self.call_action_views(request, *args, **kwargs) def list_images(self, request, *args, **kwargs): params = self.get_or_post(request) allow_types_key = 'imageManagerAllowFiles' list_size_key = 'imageManagerListSize' list_path_key = 'imageManagerListPath' list_size = int(params.get('size', self.upload_config.get(list_size_key, 7))) list_start = int(params.get("start", 0)) listpath = self.upload_config.get(list_path_key, '') allow_types = self.upload_config.get(allow_types_key, '') subdirs = self._project_flow_node_dirname(params.get('project_id', ''), params.get('flow_pk', ''), params.get('node_id', '')) files = self.get_url_files(listpath, allow_types, listpath_dirs=subdirs) if (len(files) == 0): return_info = {"state": "未找到匹配文件!", "list": [], "start": list_start, "total": 0} else: return_info = {"state": "SUCCESS", "list": files[list_start:list_start + list_size], "start": list_start, "total": len(files)} print(json.dumps(return_info)) return HttpResponse(json.dumps(return_info), content_type="application/javascript") def remove_file(self, request, *args, **kwargs): state = True msg = '' params = self.get_or_post(request) subdirs = self._project_flow_node_dirname(params.get('project_id', ''), params.get('flow_pk', ''), params.get('node_id', '')) suburlpath = unquote(params.get('path', '')) parts = suburlpath.split('/') root_dir = self.SERVER_URL['root_dir'] if parts: fpath = subdirs fpath.append(parts[-1]) filepath = os.path.join(root_dir, *fpath) try: if os.path.isfile(filepath): os.remove(filepath) except Exception as e: msg = str(e) state = False res = dict(state=state, msg=msg) dumps_params = dict(ensure_ascii=False) return JsonResponse(res, json_dumps_params=dumps_params) def list_files(self, request, *args, **kwargs): """列出文件""" params = self.get_or_post(request) allow_types_key = 'fileManagerAllowFiles' list_size_key = 'fileManagerListSize' list_path_key = 'fileManagerListPath' list_size = int(params.get('size', self.upload_config.get(list_size_key, 7))) list_start = int(params.get("start", 0)) listpath = self.upload_config.get(list_path_key, '') allow_types = self.upload_config.get(allow_types_key, '') subdirs = self._project_flow_node_dirname(params.get('project_id', ''), params.get('flow_pk', ''), params.get('node_id', '')) files = self.get_url_files(listpath, allow_types, listpath_dirs=subdirs) if (len(files) == 0): return_info = {"state": "未找到匹配文件!", "list": [], "start": list_start, "total": 0} else: return_info = {"state": "SUCCESS", "list": files[list_start:list_start + list_size], "start": list_start, "total": len(files)} return HttpResponse(json.dumps(return_info), content_type="application/javascript") def get_url_files(self, listpath, allow_types=[], listpath_dirs=[]): urlsep = '/' listpath_dirs.extend(listpath.split(urlsep)) dirlist = listpath_dirs url_files = [] root_dir = self.SERVER_URL['root_dir'] rd_parts = root_dir.split(os.sep) plength = len(rd_parts) root_url = self.SERVER_URL['value'] if dirlist: dirpath = os.path.join(root_dir, os.sep.join(dirlist)) items = os.listdir(dirpath) for item in items: if os.path.isfile(os.path.join(dirpath, item)): name, ext = os.path.splitext(item) urlparts = dirpath.split(os.sep) urlparts.append(item) urlparts = urlparts[plength:] urlparts.insert(0, root_url) url = quote(urlsep.join(urlparts)) url = url if url.startswith(urlsep) else (urlsep + url) if allow_types: if ext in allow_types: url_files.append({"url": url}) else: url_files.append({"url": url}) return url_files def catch_remote_image(self, request, *args, **kwargs): """远程抓图,当catchRemoteImageEnable:true时,如果前端插入图片地址与当前web不在同一个域,则由本函数从远程下载图片到本地 """ state = "SUCCESS" path_format_key = 'catcherPathFormat' params = self.get_or_post(request) allow_type = list(params.get("catcherAllowFiles", self.upload_config.get("catcherAllowFiles", ""))) # max_size = int(params.get("catcherMaxSize", self.upload_config.get("catcherMaxSize", 0))) remote_urls = params.getlist(self.upload_config['catcherFieldName'], []) catcher_infos = [] for remote_url in remote_urls: # 取得上传的文件的原始名称 remote_file_name = os.path.basename(remote_url) remote_original_name, remote_original_ext = os.path.splitext(remote_file_name) # 文件类型检验 if remote_original_ext in allow_type: path_format = params.get(path_format_key, self.upload_config.get(path_format_key, '')) formatter = UeditorPathFormatter() formatter.format(remote_original_name, path_format) subdirectories = self._project_flow_node_dirname(params.get('project_id', ''), params.get('flow_pk', ''), params.get('node_id', '')) formatter.set_url_path_prefix(*subdirectories) # 项目 流程 节点 构成的路径 full_file_path = self.create_full_file_path(formatter.save_subpath) self._create_directory(full_file_path) # 读取远程图片文件 try: remote_image = urlopen(remote_url) # 将抓取到的文件写入文件 try: f = open(full_file_path, 'wb') f.write(remote_image.read()) f.close() state = "SUCCESS" except Exception as E: state = u"写入抓取图片文件错误:%s" % E.message except Exception as E: state = u"抓取图片错误:%s" % E.message formatter.set_url_path_prefix(self.SERVER_URL['value']) # 添加根路径 catcher_infos.append({ "state": state, "url": quote(formatter.url_path), "size": os.path.getsize(full_file_path), "title": os.path.basename(full_file_path), "original": remote_file_name, "source": remote_url }) return_info = {"state": "SUCCESS" if len(catcher_infos) > 0 else "ERROR", "list": catcher_infos} return HttpResponse(json.dumps(return_info, ensure_ascii=False), content_type="application/javascript") @classmethod def get_db_object(cls, model_klass, **db_fields): obj = None try: obj = model_klass.objects.get(**db_fields) except ObjectDoesNotExist: obj = None return obj def _project_flow_node_dirname(self, project_id=None, flow_id=None, node_id=None, sep='_'): dirname_list = [] if node_id: node = self.get_db_object(models.Node, id=node_id) dirname_list.append(sep.join([str(node.flow.project.id), node.flow.project.name])) dirname_list.append(sep.join([node.flow.number])) dirname_list.append(sep.join([node.name])) elif flow_id: flow = self.get_db_object(models.Flow, id=flow_id) dirname_list.append(sep.join([str(flow.project.id), flow.project.name])) dirname_list.append(sep.join([flow.number])) elif project_id: project = self.get_db_object(models.Project, id=project_id) dirname_list.append(sep.join([project_id, project.name])) else: pass return dirname_list def general_upload_file(self, request, *args, **kwargs): """上传文件""" state = "SUCCESS" params = self.get_or_post(request) action = params.get('action') if action == self.upload_config.get('imageActionName'): submit_field_key = 'imageFieldName' max_size_field = 'imageMaxSize' ext_field = 'imageAllowFiles' path_format_key = 'imagePathFormat' elif action == self.upload_config.get('fileActionName'): submit_field_key = 'fileFieldName' max_size_field = 'fileMaxSize' ext_field = 'fileAllowFiles' path_format_key = 'filePathFormat' elif action == self.upload_config.get('videoActionName'): submit_field_key = 'videoFieldName' max_size_field = 'videoMaxSize' ext_field = 'videoAllowFiles' path_format_key = 'videoPathFormat' else: return JsonResponse({'state': 'action传值错误'}) submit_field_name = params.get(submit_field_key, self.upload_config.get(submit_field_key, 'upfile')) submit_file = request.FILES.get(submit_field_name, None) if submit_file is None: return JsonResponse({'state': '未上传任何文件'}) submit_file_name = submit_file.name submit_file_size = submit_file.size submit_file_ext = os.path.splitext(submit_file_name)[1] support_file_exts = list(params.get(ext_field, self.upload_config.get(ext_field, ""))) if submit_file_ext not in support_file_exts: state = "服务器只支持以下类型的文件:{},实际上传的是:{}".format(' | '.join(support_file_exts), submit_file_name) return JsonResponse({'state': state}) limit_size = int(params.get(max_size_field, self.upload_config.get(max_size_field, self.DEFAULT_MAX_SIZE))) asize = FileSize(submit_file_size) lsize = FileSize(limit_size) if asize > lsize: state = "上传文件大小({})已超过最大限制({})%s。".format(asize.human_readable, lsize.human_readable) return JsonResponse({'state': state}) path_format = params.get(path_format_key, self.upload_config.get(path_format_key, '')) formatter = UeditorPathFormatter() formatter.format(submit_file_name, path_format) subdirectories = self._project_flow_node_dirname(params.get('project_id', ''), params.get('flow_pk', ''), params.get('node_id', '')) formatter.set_url_path_prefix(*subdirectories) # 项目 流程 节点 构成的路径 full_file_path = self.create_full_file_path(formatter.save_subpath) try: self._create_directory(full_file_path) with open(full_file_path, 'wb') as f: for chunk in submit_file.chunks(): f.write(chunk) except Exception as e: state = "保存文件{}错误: {}".format(submit_file_name, str(e)) formatter.set_url_path_prefix(self.SERVER_URL['value']) # 添加根路径 res = dict(state=state, url=quote(formatter.url_path), title=submit_file_name, original=submit_file_name) dumps_params = dict(ensure_ascii=False) return JsonResponse(res, json_dumps_params=dumps_params) def _create_directory(self, path): target_dir = os.path.dirname(path) if not os.path.exists(target_dir): os.makedirs(target_dir) def upload_scrawl(self, request, *args, **kwargs): """上传涂鸦""" state = "SUCCESS" sfn = 'scrawlFieldName' max_size_field = 'scrawlMaxSize' scrawl_file_name = kwargs.get('scrawl_file_name', '涂鸦.png') params = self.get_or_post(request) field_name = params.get(sfn, self.upload_config.get(sfn, 'upfile')) base64_content = params.get(field_name) content = base64.b64decode(base64_content) actual_size = len(content) limit_size = int(params.get(max_size_field, self.upload_config.get(max_size_field, 0))) asize = FileSize(actual_size) lsize = FileSize(limit_size) if asize > lsize: state = "上传文件大小({})已超过最大限制({})%s。".format(asize.human_readable, lsize.human_readable) return JsonResponse({'state': state}) path_format = params.get('scrawlPathFormat', self.upload_config.get('scrawlPathFormat', '')) formatter = UeditorPathFormatter() formatter.format(scrawl_file_name, path_format) subdirectories = self._project_flow_node_dirname(params.get('project_id', ''), params.get('flow_pk', ''), params.get('node_id', '')) formatter.set_url_path_prefix(*subdirectories) # 项目 流程 节点 构成的路径 full_file_path = self.create_full_file_path(formatter.save_subpath) try: self._create_directory(full_file_path) with open(full_file_path, 'wb') as f: f.write(content) except Exception as e: state = "写入图片文件错误: {}".format(str(e)) formatter.set_url_path_prefix(self.SERVER_URL['value']) # 添加根路径 res = dict(state=state, url=quote(formatter.url_path), title=scrawl_file_name, original=scrawl_file_name) dumps_params = dict(ensure_ascii=False) return JsonResponse(res, json_dumps_params=dumps_params) def save_ueditor_content(self, request, *args, **kwargs): state = True msg = "" submit_datas = self.get_or_post(request) project_id = submit_datas.get('project_id', '') flow_id = submit_datas.get('flow_pk', '') node_id = submit_datas.get('node_id', '') ueditor_content = submit_datas.get("ueditor_content") node = self.get_db_object(models.Node, id=node_id) if node: if node.is_review_kind or node.is_env_kind or node.is_test_kind: r = self.get_db_object(models.Result, node=node, batch=node.batch) if r: r.doc = ueditor_content r.save() else: code, name = (-1, "还没有结果") nr = models.Result(code=code, name=name, node=node, batch=node.batch, creator=request.user, doc=ueditor_content) nr.save() else: state = False msg = "节点({})不支持".format(node.name) else: flow = self.get_db_object(models.Flow, id=flow_id) if flow: flow.doc = ueditor_content flow.save() else: project = self.get_db_object(models.Project, id=project_id) if project: project.doc = ueditor_content project.save() else: state = False # 项目 流程 节点 任意一个都找不到,不知道保存到哪 msg = "error code: p{}f{}n{}".format(project_id, flow_id, node_id) res = dict(state=state, msg=msg) dumps_params = dict(ensure_ascii=False) return JsonResponse(res, json_dumps_params=dumps_params)
#!/usr/bin/env python # -*- encoding: utf-8 -*- ''' @Author: 思文伟 @Date: 2021/03/07 14:47:32 ''' from django.views import View from django.conf import settings from django.views import static from django.http import HttpResponseRedirect from django.utils.decorators import method_decorator from django.views.decorators.csrf import csrf_exempt @method_decorator(csrf_exempt, name='dispatch') class Ueditor_FileServer(View): suburl_group_name = 'suburl' suburl = '(?P<{}>.+)'.format(suburl_group_name) redirect_url = "/login" @property def ueditor_file_dir(self): return settings.UEDITOR_FILE_DIR def download(self, request, *args, **kwargs): if not request.user.is_authenticated: return HttpResponseRedirect(self.redirect_url) sub_url = kwargs.get(self.suburl_group_name) document_root = self.ueditor_file_dir path = sub_url res = static.serve(request, path, document_root=document_root) return res def get(self, request, *args, **kwargs): return self.download(request, *args, **kwargs) def post(self, request, *args, **kwargs): return self.download(request, *args, **kwargs)
#!/usr/bin/env python # -*- encoding: utf-8 -*- ''' @Author: 思文伟 @Date: 2021/03/07 14:48:18 ''' class FileSize(): SIZE_UNIT = {"Byte": 1, "KB": 1024, "MB": 1048576, "GB": 1073741824, "TB": 1099511627776} def __init__(self, size): self.size = int(FileSize.Format(size)) @staticmethod def Format(size): import re if isinstance(size, int): return size else: if not isinstance(size, str): return 0 else: oSize = size.lstrip().upper().replace(" ", "") pattern = re.compile(r"(\d*\.?(?=\d)\d*)(byte|kb|mb|gb|tb)", re.I) match = pattern.match(oSize) if match: m_size, m_unit = match.groups() if m_size.find(".") == -1: m_size = int(m_size) else: m_size = float(m_size) if m_unit != "BYTE": return m_size * FileSize.SIZE_UNIT[m_unit] else: return m_size else: return 0 # 返回字节为单位的值 @property def size(self): return self._size @size.setter def size(self, newsize): try: self._size = int(newsize) except Exception: self._size = 0 # 返回带单位的自动值 @property def human_readable(self): if self.size < FileSize.SIZE_UNIT["KB"]: unit = "Byte" elif self.size < FileSize.SIZE_UNIT["MB"]: unit = "KB" elif self.size < FileSize.SIZE_UNIT["GB"]: unit = "MB" elif self.size < FileSize.SIZE_UNIT["TB"]: unit = "GB" else: unit = "TB" if (self.size % FileSize.SIZE_UNIT[unit]) == 0: return "%s%s" % ((self.size / FileSize.SIZE_UNIT[unit]), unit) else: return "%0.2f%s" % (round(float(self.size) / float(FileSize.SIZE_UNIT[unit]), 2), unit) def __str__(self): return self.human_readable # 相加 def __add__(self, other): if isinstance(other, FileSize): return FileSize(other.size + self.size) else: return FileSize(FileSize(other).size + self.size) def __sub__(self, other): if isinstance(other, FileSize): return FileSize(self.size - other.size) else: return FileSize(self.size - FileSize(other).size) def __gt__(self, other): if isinstance(other, FileSize): if self.size > other.size: return True else: return False else: if self.size > FileSize(other).size: return True else: return False def __lt__(self, other): if isinstance(other, FileSize): if other.size > self.size: return True else: return False else: if FileSize(other).size > self.size: return True else: return False def __ge__(self, other): if isinstance(other, FileSize): if self.size >= other.size: return True else: return False else: if self.size >= FileSize(other).size: return True else: return False def __le__(self, other): if isinstance(other, FileSize): if other.size >= self.size: return True else: return False else: if FileSize(other).size >= self.size: return True else: return False
#!/usr/bin/env python # -*- encoding: utf-8 -*- ''' @Author: 思文伟 @Date: 2021/03/07 14:39:03 ''' import re import os import random import datetime class UeditorPathFormatter(object): def __init__(self, url_path_sep='/'): self.url_path_sep = url_path_sep self.url_path_prefix = None self.url_path = None def _replace(self, file_name, rand_prefix='rand:', dtime=datetime.datetime.now()): def wrapper(match_obj): ms = match_obj.group(0) if ms == '{filename}': repl = file_name elif ms == '{time}': repl = str(int(dtime.timestamp())) elif ms == '{yyyy}': repl = dtime.strftime('%Y') elif ms == '{yy}': repl = dtime.strftime('%y') elif ms == '{mm}': repl = dtime.strftime('%m') elif ms == '{dd}': repl = dtime.strftime('%d') elif ms == '{hh}': repl = dtime.strftime('%H') elif ms == '{ii}': repl = dtime.strftime('%M') elif ms == '{ss}': # datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f') # 含微秒的日期时间 repl = dtime.strftime('%S') else: ms_content = ms.lstrip("{").rstrip("}") if ms_content.startswith(rand_prefix): length = int(ms_content[len(rand_prefix):]) number = random.randint(1, int('9' * length)) fmt = '{:>0%sd}' % length repl = fmt.format(number) else: repl = ms return repl return wrapper def _check_path_format(self, path_format): pass def _check_file_name(self, file_name): pass def format(self, file_name, path_format, endswith_filename=True): """ # {filename} # 会替换成文件名 [要注意中文文件乱码问题] # {rand:6} # 会替换成随机数,后面的数字是随机数的位数 # {time} # 会替换成时间戳 # {yyyy} # 会替换成四位年份 # {yy} # 会替换成两位年份 # {mm} # 会替换成两位月份 # {dd} # 会替换成两位日期 # {hh} # 会替换成两位小时 # {ii} # 会替换成两位分钟 # {ss} # 会替换成两位秒 Args: file_name: 文件名(含拓展名) eg: index.html path_format: ueditor 相关项的PathFormat 参见 http://fex.baidu.com/ueditor/#server-path endswith_filename: 控制path_format 不是以文件名结尾的话是否自动附加文件名结尾, True - 附加 """ self.reset() parts = [ "\\{filename\\}", "\\{rand:\\d+\\}", "\\{time\\}", "\\{yyyy\\}", "\\{yy\\}", "\\{mm\\}", "\\{dd\\}", "\\{hh\\}", "\\{ii\\}", "\\{ss\\}", ] file_name_fmt = '{filename}' regex = '(' + '|'.join(parts) + ')' matcher = re.compile(regex) if endswith_filename and not path_format.endswith(file_name_fmt): path_format = '/'.join([path_format, file_name_fmt]) self.url_path = matcher.sub(self._replace(file_name), path_format) return self.url_path @property def save_subpath(self): try: p = self.url_path except AttributeError: raise AttributeError('Please call format method before calling save_subpath.') parts = p.split(self.url_path_sep) return os.path.join('', *parts) def set_url_path_prefix(self, *directories): self.url_path_prefix = self.url_path_sep.join(directories) if not self.url_path_prefix.startswith(self.url_path_sep): self.url_path_prefix = self.url_path_sep + self.url_path_prefix if self.url_path.startswith(self.url_path_sep): self.url_path = self.url_path_prefix + self.url_path else: self.url_path = self.url_path_prefix + self.url_path.lstrip(self.url_path_sep) def reset(self): self.url_path_prefix = None self.url_path = None if __name__ == '__main__': formatter = UeditorPathFormatter() formatter.format('index.html', "/ueditor/php/upload/image/{yyyy}{mm}{dd}/{time}/{rand:11}/{filename}") print(formatter.url_path) print(formatter.save_subpath)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。