Use Baidu ueeditor rich text editor in django project

Posted by jami3 on Fri, 04 Mar 2022 22:50:34 +0100

Get ueeditor

Download UEditor source code from the official website. There is no UEditor in the downloaded source code all. JS file. You need to use grunt to package the source package into a deployment Version (including ueditor.all.js file)

  1. Install node js
  2. Global install grunt (NPM install - G grunt CLI)
  3. The command line switches the directory to the ueeditor directory (including the Gruntfile.js file)
  4. npm install installation dependency
  5. Execute the command grunt default, which will package the source file and generate ueditor all. JS file. After execution, a dist directory will be generated under the ueditor directory
  6. There is a utf8 PHP directory in dist, which is the deployment version. Modify and rename it ueditor
ueditor related requests to be processed by the server

Put the UEditor deployment version into the static directory of the project, and then introduce UEditor on the relevant pages to be used according to the instructions of the official website config. JS and ueeditor all. JS file, you can use most of the functions of ueeditor. Only uploading files, pictures, videos, online pictures and online files, which are related to uploading, are not easy to use. Because these functions need the support of the back-end server, we need to implement the response view function in our django project to process these requests:

  1. Request for obtaining backend configuration item information (the parameter carried in the request address is action=config)
  2. Upload picture request (the parameters carried by the request address can be seen from the specific value set by the backend configuration item. The default is: action=uploadimage)
  3. Upload video request (the parameters carried by the request address can be seen from the specific value set by the backend configuration item. The default is: action=uploadvideo)
  4. Upload attachment request (the parameters carried by the request address can be seen from the specific value set by the backend configuration item. The default is: action=uploadfile)
  5. List all picture requests (the parameters carried by the request address can be seen from the specific value set by the backend configuration item. The default is: action=listimage)
  6. List all attachment requests (the parameters carried by the request address are based on the specific value set by the backend configuration item. The default is: action=listfile)
  7. Request for uploading graffiti pictures (the parameters carried by the request address can be seen from the specific value set by the backend configuration item. The default is: action=uploadscrawl)
  8. The default value of the back-end configuration parameter is: chimage = action (the default value of the configuration parameter is chimage)

For uploading pictures, screenshots, videos, attachments and graffiti pictures, the JSON format data returned by the background server is as follows:

{
    "state": "SUCCESS",                         // Status information. When successful, the return value is fixed as 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": "Latest bill of materials ID Display error 001.png",      // File name
    "original": "Latest bill of materials ID Display error 001.png"    // Internal file name, which is generally the same as titile
}

When capturing remote pictures, the JSON format data that the background server should return is as follows:

{
    "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 ", / / download address
        }
    ]
}

List the pictures and files in the specified directory. The JSON format data that the background server should return is as follows:

{
    "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
}
The django project handles the ueditor request code

The following is the relevant code for processing the above ueditor requests in the transfer test process management system I developed

  • settings.py configuration

  • Project routing configuration (urls.py)

# -*-Coding: UTF-8 - * - '' 'created on December 15, 2019

@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()),  # Handle ueditor's request to obtain server configuration, upload pictures, upload attachments, list all pictures, list all attachments, delete pictures, delete attachments, etc
    re_path(r'^{}/{}$'.format(views.UEditor.SERVER_URL['value'], views.Ueditor_FileServer.suburl), views.Ueditor_FileServer.as_view()),
# Processing the request of ueditor to obtain various files is actually providing the service of obtaining static files]

  • Handle various upload requests, image list and attachment list request views (ueditor.py)
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@Author: Si Wenwei
@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}

    # Upload picture configuration item
    IMAGE = {
        "imageActionName": "uploadimage",  # The name of the action to upload the picture
        "imageMaxSize": 20485760,  # Upload size limit, unit B,10M
        "imageFieldName": "upfile",  # *Submitted picture form name
        "imageUrlPrefix": "",
        "imagePathFormat": "",
        "imageAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"],  # Upload picture format display
    }

    # Graffiti picture upload configuration item
    SCRAWL = {
        "scrawlActionName": "uploadscrawl",  # Name of action to upload graffiti
        "scrawlFieldName": "upfile",  # Submitted picture form name
        "scrawlMaxSize": 10485760,  # Upload size limit, unit B 10m
        "scrawlUrlPrefix": "",
        "scrawlPathFormat": "",
    }

    # Screenshot tool upload
    SNAPSCREEN = {
        "snapscreenActionName": "uploadimage",  # The name of the action to upload the screenshot
        "snapscreenPathFormat": "",
        "snapscreenUrlPrefix": "",
    }

    # Grab remote picture configuration
    CATCHER = {
        "catcherLocalDomain": ["127.0.0.1", "localhost", "img.baidu.com"],
        "catcherPathFormat": "",
        "catcherActionName": "catchimage",  # The action name of the remote image to be captured
        "catcherFieldName": "source",  # Submitted picture list form name
        "catcherMaxSize": 10485760,  # Upload size limit, unit B
        "catcherAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"],  # Capture picture format display
        "catcherUrlPrefix": "",
    }

    # Upload video configuration
    VIDEO = {
        "videoActionName": "uploadvideo",  # The name of the action to upload the video
        "videoPathFormat": "",
        "videoFieldName": "upfile",  # Name of submitted video form
        "videoMaxSize": 102400000,  # Upload size limit, unit B, default 100MB
        "videoUrlPrefix": "",
        "videoAllowFiles": [".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg", ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid"],  # Upload video format display
    }

    # Upload file configuration
    __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",  # In the controller, the name of the action to upload the video
        "filePathFormat": "",
        "fileFieldName": "upfile",  # File form name submitted
        "fileMaxSize": 204800000,  # Upload size limit, unit B, 200MB
        "fileUrlPrefix": "",  # File access path prefix
        "fileAllowFiles": __FILE_ALLOW_FILES,  # Upload file format display
    }

    # List the pictures in the specified directory
    IMAGE_MANAGER = {
        "imageManagerActionName": "listimages",  # The name of the action to perform image management
        "imageManagerListPath": "",
        "imageManagerListSize": 30,  # Number of documents listed each time
        # File types listed
        "imageManagerAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"],
        "imageManagerUrlPrefix": "",  # Picture access path prefix
    }

    # Lists the files in the specified directory
    __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 name of executing file management
        "fileManagerListPath": "",
        "fileManagerUrlPrefix": "",
        "fileManagerListSize": 30,  # Number of documents listed each time
        "fileManagerAllowFiles": __FILE_MANAGER_ALLOW_FILES  # File types listed
    }

    DEFAULT_MAX_SIZE = 20485760

    IMAGE_REMOVE = {"imageRemoveActionName": "removeimage"}
    FILE_REMOVE = {"fileRemoveActionName": "removefile"}
    SAVE_TO_SERVER = {"saveToServerActionName": "savecontent"}

    @property
    def upload_config(self):
        """Upload configuration item returned to the front end"""

        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):
        """Configuration correspondence action View function for"""

        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 = 'Not for{}Configure the corresponding view function'.format(action)
            return HttpResponseBadRequest(message)

    def create_full_file_path(self, subpath):
        """structure ueditor Upload storage directory

        Args:
            subpath: Sub path

        """
        return os.path.join(self.ROOT_DIR, subpath)

    def get_ueditor_config(self, request, *args, **kwargs):
        """Return to configuration"""

        return HttpResponse(self.to_json(self.upload_config), content_type="application/javascript")

    def get(self, request, *args, **kwargs):
        """obtain ueditor Back end of URL address"""

        return self.call_action_views(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        """obtain ueditor Back end of URL address"""

        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": "No matching file found!", "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):
        """List files"""

        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": "No matching file found!", "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):
        """Remote capture, when catchRemoteImageEnable:true If the front-end inserts the picture address and the current web If it is not in the same domain, this function downloads the image from the remote to the local
        """

        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:

            # Get the original name of the uploaded file
            remote_file_name = os.path.basename(remote_url)
            remote_original_name, remote_original_ext = os.path.splitext(remote_file_name)

            # Document type inspection
            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)  # Path composed of project process nodes
                full_file_path = self.create_full_file_path(formatter.save_subpath)
                self._create_directory(full_file_path)

                # Read remote picture file
                try:
                    remote_image = urlopen(remote_url)
                    # Write the captured file to the file
                    try:
                        f = open(full_file_path, 'wb')
                        f.write(remote_image.read())
                        f.close()
                        state = "SUCCESS"
                    except Exception as E:
                        state = u"Error writing captured picture file:%s" % E.message
                except Exception as E:
                    state = u"Image capture error:%s" % E.message
                formatter.set_url_path_prefix(self.SERVER_URL['value'])  # Add root path
                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):
        """Upload file"""

        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 Value transmission error'})

        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': 'No files uploaded'})
        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 = "The server only supports the following types of files:{},The actual upload is:{}".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 = "Upload file size({})The maximum limit has been exceeded({})%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)  # Path composed of project process nodes
        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 = "Save file{}error: {}".format(submit_file_name, str(e))
        formatter.set_url_path_prefix(self.SERVER_URL['value'])  # Add root path
        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):
        """Upload graffiti"""

        state = "SUCCESS"
        sfn = 'scrawlFieldName'
        max_size_field = 'scrawlMaxSize'
        scrawl_file_name = kwargs.get('scrawl_file_name', 'Graffiti.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 = "Upload file size({})The maximum limit has been exceeded({})%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)  # Path composed of project process nodes
        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 = "Error writing picture file: {}".format(str(e))
        formatter.set_url_path_prefix(self.SERVER_URL['value'])  # Add root path
        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, "No results yet")
                    nr = models.Result(code=code, name=name, node=node, batch=node.batch, creator=request.user, doc=ueditor_content)
                    nr.save()
            else:
                state = False
                msg = "node({})I won't support it".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
                    # No one of the project process nodes can be found. I don't know where to save it
                    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)

  • Provide a service view for ueditor to obtain static files (ueditor_fileserver.py)
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@Author: Si Wenwei
@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)

  • Processing file size (filesize.py)
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@Author: Si Wenwei
@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

    # Returns a value in bytes
    @property
    def size(self):
        return self._size

    @size.setter
    def size(self, newsize):
        try:
            self._size = int(newsize)
        except Exception:
            self._size = 0

    # Returns an automatic value with units
    @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

    # Add up
    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

  • Handle PathFormat related items in ueditor (ueditor_path_formatter.py)
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@Author: Si Wenwei
@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') # Date and time with microseconds
                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}  # It will be replaced by the file name [pay attention to the problem of garbled Chinese documents]
        # {rand:6}    # Will be replaced by a random number, followed by the number of digits of the random number
        # {time}      # Will be replaced with a timestamp
        # {yyyy}      # Will be replaced with a four digit year
        # {yy}        # Will be replaced by two digit years
        # {mm}        # Will be replaced by two digit months
        # {dd}        # Will be replaced with two digit dates
        # {hh}        # Will be replaced by two hours
        # {ii}        # Will be replaced by two minutes
        # {ss}        # Will be replaced by two bits of seconds

        Args:
            file_name: File name (including extended name) eg: index.html
            path_format: ueditor Of related items PathFormat See http://fex.baidu.com/ueditor/#server-path
            endswith_filename: control path_format If it does not end with a file name, whether to automatically append the end of the file name, True - additional
        """

        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)

Experience address

http://116.63.153.101:8005/

Topics: Python Javascript Django css ueditor