springboot集成百度编辑器ueditor

ueditor是百度一款所见即所得编辑器,它支持丰富的富文本编辑场景,那么如何在springboot中集成呢。

1. 首先我们去ueditor git仓库下载最新版本的代码,https://github.com/fex-team/ueditor ,这里推荐选择最新的tag进行下载。下载完成后需要进行打包,这一步编辑器官网已经有详细说明

执行打包grunt命令时,可以传入编码和后台语言的参数

支持两种编码指定:--encode参数
utf8 (默认编码)
gbk
提供四种后台语言支持:--server参数

php (默认语言)
jsp
net (代表.net后台)
asp
例如:想要打包成编码是gbk,后台语言是asp版本,可执行命令:

 grunt --encode=gbk --server=asp

与springboot集成我们需要打包为server=jsp,打包完成后将目录中会生成dist/utf8-jsp文件夹,我们将该文件夹中的内容全部拷贝到前端页面所在的位置。将jsp下的文件拷贝至后端代码工程中

 

2.第一步我们已经完成了资源的打包,现在我们开始进行配置。ueditor中的配置信息是通过后端提供的接口进行动态读取,具体的配置信息在第一步打包生成的dist/utf8-jsp/jsp 下的config.json文件中,这里包含了编辑器操作对应后端接口actionName及一些动态属性。

3.统一后端服务接口

/**
     * 配置项主体。注意,此处所有涉及到路径的配置别遗漏URL变量。
     */
    window.UEDITOR_CONFIG = {

        //为编辑器实例添加一个路径,这个不能被注释
        UEDITOR_HOME_URL: URL

        // 服务器统一请求接口路径
        , serverUrl: URL + "jsp/controller.jsp"

这里后段接口默认是项目地址/jsp/controller.jsp , 与springboot集成则需要提供一个后端接口来统一处理请求

@Slf4j
@RestController
@RequestMapping("/editor")
public class EditorController {

    /**
     * 配置
     *
     * @param columnId
     * @return
     */
    @RequestMapping({"/", ""})
    public void index(HttpServletRequest request, HttpServletResponse response, String columnId) throws Exception {
        if (StringUtils.isBlank(columnId)) {
            log.error("请求中缺少columnId");
            return;
        }
        DocumentContextUtil.Context context = DocumentContextUtil.getContext(columnId);
        List<String> imageTypes = context.getPicType().stream().map(item -> "." + item).collect(Collectors.toList());
        List<String> fileTypes = context.getFileType().stream().map(item -> "." + item).collect(Collectors.toList());
        String cmsDirectory = context.getCmsDirectory();
        String uploadDirectory = context.getUploadDirectory();

        EditorConf editConf = new EditorConf(context.getPicSize(), imageTypes, context.getFileSize(), fileTypes, cmsDirectory, uploadDirectory);
        response.getWriter().write(new ActionEnter(request, editConf).exec());
    }

}

上面代码是一个统一处理请求服务端地址,这里会通过ActionEnter来处理具体的请求

public ActionEnter(HttpServletRequest request, EditorConf conf) {
        this.request = request;
        this.actionType = request.getParameter("action");
        this.configManager = ConfigManager.getInstance(conf);
    }

此处的请求中的action参数为编辑器对应操作的标识,与config.json中的xxxActionName对应。

这里我们有两种选择,1.直接复用这个config.json配置将son返回给前端 。 2.定义自己的配置信息通过json形式返回给前端。因为我们的配置在一般情况下不是固定的是动态的,需要根据某个参数去数据库查询得到,所以我们这里采用自定义配置方式

@Data
public class EditorConf {

    private ImageConf imageConf;
    private ScrawlConf scrawlConf;
    private CatcherConf catcherConf;
    private VideoConf videoConf;
    private SnapScreenConf snapScreenConf;
    private FileConf fileConf;
    private AudioConf audioConf;
    private ImageManagerConf imageManagerConf;
    private FileManagerConf fileManagerConf;

    private String directory;
    private String uploadDirectory;

    public EditorConf(int imageSize, List<String> imageTypes, int fileSize, List<String> fileTypes, String directory, String uploadDirectory) {
        this.imageConf = new ImageConf();
        this.imageConf.setImageMaxSize(imageSize * 1024);
        this.imageConf.setImageAllowFiles(imageTypes);

        this.scrawlConf = new ScrawlConf();
        this.scrawlConf.setScrawlMaxSize(imageSize * 1024);

        this.catcherConf = new CatcherConf();
        this.catcherConf.setCatcherMaxSize(imageSize * 1024);
        this.catcherConf.setCatcherAllowFiles(imageTypes);

        this.videoConf = new VideoConf();
        this.videoConf.setVideoMaxSize(fileSize * 1024);

        this.snapScreenConf = new SnapScreenConf();

        this.fileConf = new FileConf();
        this.fileConf.setFileMaxSize(fileSize * 1024);
        this.fileConf.setFileAllowFiles(fileTypes);

        this.audioConf = new AudioConf();
        this.imageManagerConf = new ImageManagerConf();
        this.fileManagerConf = new FileManagerConf();

        this.directory = directory;
        this.uploadDirectory = uploadDirectory;
    }

    public JSONObject getConfJson() {
        JSONObject json = new JSONObject();
        json.putAll(getConfMap(this.imageConf));
        json.putAll(getConfMap(this.scrawlConf));
        json.putAll(getConfMap(this.catcherConf));
        json.putAll(getConfMap(this.videoConf));
        json.putAll(getConfMap(this.snapScreenConf));
        json.putAll(getConfMap(this.audioConf));
        json.putAll(getConfMap(this.imageManagerConf));
        json.putAll(getConfMap(this.fileManagerConf));
        return json;
    }

    private Map<String, Object> getConfMap(Object object) {
        return JSONObject.parseObject(JSONObject.toJSONString(object), new TypeReference<Map<String, Object>>() {{
        }});
    }

    /**
     * 上传图片配置项
     */
    @Data
    public static class ImageConf {
        /**
         * 执行上传图片的action名称
         */
        private String imageActionName = "uploadImage";
        /**
         * 提交的图片表单名称
         */
        private String imageFieldName = "image";
        /**
         * 上传大小限制,单位B
         */
        private long imageMaxSize = 1024 * 1024;
        /**
         * 上传图片格式显示
         */
        private List<String> imageAllowFiles = Arrays.asList(".png", ".jpg", ".jpeg", ".gif", ".bmp");
        /**
         * 是否压缩图片,默认是true
         */
        private boolean imageCompressEnable = false;
        /**
         * 图片压缩最长边限制
         */
        private int imageCompressBorder = 1600;
        /**
         * 插入的图片浮动方式
         */
        private String imageInsertAlign = "none";
        /**
         * 图片访问路径前缀
         */
        private String imageUrlPrefix = "";
        /**
         * 上传保存路径,可以自定义保存路径和文件名格式
         */
        private String imagePathFormat = "/image/{yyyy}{mm}{dd}/{time}{rand:6}";
    }

    /**
     * 涂鸦图片上传配置项
     */
    @Data
    public static class ScrawlConf {
        /**
         * 执行上传图片的action名称
         */
        private String scrawlActionName = "uploadScrawl";
        /**
         * 提交的图片表单名称
         */
        private String scrawlFieldName = "scrawl";
        /**
         * 上传大小限制,单位B
         */
        private long scrawlMaxSize = 1024 * 1024;
        /**
         * 插入的图片浮动方式
         */
        private String scrawlInsertAlign = "none";
        /**
         * 图片访问路径前缀
         */
        private String scrawlUrlPrefix = "";
        /**
         * 上传保存路径,可以自定义保存路径和文件名格式
         */
        private String scrawlPathFormat = "/image/{yyyy}{mm}{dd}/{time}{rand:6}";
    }

    /**
     * 截图工具上传
     */
    @Data
    public static class SnapScreenConf {
        /**
         * 执行上传图片的action名称
         */
        private String snapscreenActionName = "uploadImage";
        /**
         * 插入的图片浮动方式
         */
        private String snapscreenInsertAlign = "none";
        /**
         * 图片访问路径前缀
         */
        private String snapscreenUrlPrefix = "";
        /**
         * 上传保存路径,可以自定义保存路径和文件名格式
         */
        private String snapscreenPathFormat = "/image/{yyyy}{mm}{dd}/{time}{rand:6}";
    }

    /**
     * 抓取远程图片配置
     */
    @Data
    public static class CatcherConf {
        private List<String> catcherLocalDomain = Arrays.asList("127.0.0.1", "localhost", "img.baidu.com");
        /**
         * 执行抓取远程图片的action名称
         */
        private String catcherActionName = "catchImage";
        /**
         * 提交的图片列表表单名称
         */
        private String catcherFieldName = "source";
        /**
         * 图片访问路径前缀
         */
        private String catcherUrlPrefix = "";
        /**
         * 上传大小限制,单位B
         */
        private long catcherMaxSize = 1024 * 1024;
        /**
         * 抓取图片格式显示
         */
        private List<String> catcherAllowFiles = Arrays.asList(".png", ".jpg", ".jpeg", ".gif", ".bmp");
        /**
         * 上传保存路径,可以自定义保存路径和文件名格式
         */
        private String catcherPathFormat = "/image/{yyyy}{mm}{dd}/{time}{rand:6}";
    }

    /**
     * 上传视频配置
     */
    @Data
    public static class VideoConf {
        /**
         * 执行上传视频的action名称
         */
        private String videoActionName = "uploadVideo";
        /**
         * 提交的视频表单名称
         */
        private String videoFieldName = "video";
        /**
         * 视频访问路径前缀
         */
        private String videoUrlPrefix = "";
        /**
         * 上传大小限制,单位B,默认100MB
         */
        private long videoMaxSize = 512 * 1024 * 1024 * 1024;
        /**
         * 上传视频格式显示
         */
        private List<String> videoAllowFiles = Arrays.asList(".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg",
                ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid");
        /**
         * 上传保存路径,可以自定义保存路径和文件名格式
         */
        private String videoPathFormat = "/video/{yyyy}{mm}{dd}/{time}{rand:6}";
    }

    /**
     * 上传文件配置
     */
    @Data
    public static class FileConf {
        /**
         * 执行上传视频的action名称
         */
        private String fileActionName = "uploadFile";
        /**
         * 提交的文件表单名称
         */
        private String fileFieldName = "file";
        /**
         * 文件访问路径前缀
         */
        private String fileUrlPrefix = "";
        /**
         * 上传大小限制,单位B,默认50MB
         */
        private long fileMaxSize = 100 * 1024 * 1024 * 1024;
        /**
         * 上传文件格式显示
         */
        private List<String> fileAllowFiles = Arrays.asList(".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");
        /**
         * 上传保存路径,可以自定义保存路径和文件名格式
         */
        private String filePathFormat = "/file/{yyyy}{mm}{dd}/{time}{rand:6}";
    }

    /**
     * 上传音频配置
     */
    @Data
    public static class AudioConf {
        /**
         * 执行上传音频的action名称
         */
        private String audioActionName = "uploadAudio";
        /**
         * 提交的文件表单名称
         */
        private String audioFieldName = "audio";
        /**
         * 文件访问路径前缀
         */
        private String audioUrlPrefix = "";
        /**
         * 上传大小限制,单位B,默认50MB
         */
        private long audioMaxSize = 100 * 1024 * 1024 * 1024;
        /**
         * 上传音频格式显示
         */
        private List<String> audioAllowFiles = Arrays.asList(".mp3");
        /**
         * 上传保存路径,可以自定义保存路径和文件名格式
         */
        private String audioPathFormat = "/file/{yyyy}{mm}{dd}/{time}{rand:6}";
    }

    /**
     * 列出指定目录下的图片
     */
    @Data
    public static class ImageManagerConf {
        /**
         * 执行图片管理的action名称
         */
        private String imageManagerActionName = "listImage";
        /**
         * 指定要列出图片的目录
         */
        private String imageManagerListPath = "{path}";
        /**
         * 每次列出文件数量
         */
        private int imageManagerListSize = 20;
        /**
         * 图片访问路径前缀
         */
        private String imageManagerUrlPrefix = "";
        /**
         * 插入的图片浮动方式
         */
        private String imageManagerInsertAlign = "none";
        /**
         * 列出的文件类型
         */
        private List<String> imageManagerAllowFiles = Arrays.asList(".png", ".jpg", ".jpeg", ".gif", ".bmp");
    }

    /**
     * 列出指定目录下的文件
     */
    @Data
    public static class FileManagerConf {
        /**
         * 执行文件管理的action名称
         */
        private String fileManagerActionName = "listFile";
        /**
         * 指定要列出文件的目录
         */
        private String fileManagerListPath = "{path}";
        /**
         * 文件访问路径前缀
         */
        private String fileManagerUrlPrefix = "";
        /**
         * 每次列出文件数量
         */
        private int fileManagerListSize = 20;
        /**
         * 列出的文件类型
         */
        private List<String> fileManagerAllowFiles = Arrays.asList(".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");
    }

这个自定义配置其实是将config.json转化为java的对象,这样我们就能给配置动态赋值并返回,比如项目中配置的上传文件目录、文件大小、文件类型等都可以在初始化配置的时候读去数据库配置进行赋值,当然需要将原来jsp下的代码做一些改造。

当准备好配置信息后进入ActionEnter进行具体的请求处理,编辑器后端处理的关键是ActionEnter中的invoke方法

int actionCode = ActionMap.getType(this.actionType);
        Map<String, Object> conf = null;

        switch (actionCode) {
            case ActionMap.CONFIG:
                return this.configManager.getAllConfig().toString();
            case ActionMap.UPLOAD_IMAGE:
            case ActionMap.UPLOAD_SCRAWL:
            case ActionMap.UPLOAD_AUDIO:
            case ActionMap.UPLOAD_VIDEO:
            case ActionMap.UPLOAD_FILE:
                conf = this.configManager.getConfig(actionCode);
                state = new Uploader(request, conf).doExec();
                break;
            case ActionMap.CATCH_IMAGE:
                conf = configManager.getConfig(actionCode);
                String[] list = this.request.getParameterValues((String) conf.get("fieldName"));
                state = new ImageHunter(request, conf).capture(list);
                break;
            case ActionMap.LIST_IMAGE:
                conf = configManager.getConfig(actionCode);
                int pageNum = this.getStartIndex();
                int pageSize = this.getSize();
                String keyword = this.getKeyword();
                int imageType = this.getImageType();
                state = new ImageManager(conf).listFile(pageNum, pageSize, getSiteid(), keyword, imageType);
                break;
            case ActionMap.LIST_FILE:
                conf = configManager.getConfig(actionCode);
                int start = this.getStartIndex();
                state = new FileManager(conf).listFile(start);
                break;
        }

我们将上传文件、拉取服务端图片/文件等不同的操作对应不同的处理类,下面是我们对上传文件处理类做改造后的代码

public class BinaryUploader {

    public static final State save(HttpServletRequest request, Map<String, Object> conf) {
        ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());

        boolean isAjaxUpload = request.getHeader("X_Requested_With") != null;
        if (isAjaxUpload) {
            upload.setHeaderEncoding("UTF-8");
        }

        long maxSize = (long) conf.get("maxSize");
        List<String> allowFiles = (List<String>) conf.get("allowFiles");
        String fieldName = (String) conf.get("fieldName");
        String uploadDirectory = (String) conf.get("uploadDirectory");

        try {
            MultipartFile multipartFile = ((MultipartHttpServletRequest) request).getFile(fieldName);
            if (multipartFile == null) {
                return new BaseState(false, AppInfo.NOTFOUND_UPLOAD_DATA);
            }
            // 用来表示是不是编辑器中上传的图片
            String orgName = multipartFile.getOriginalFilename();
            String extName = FileUtil.getExtName(orgName);

            if (!FileUtil.allowFileType(extName, allowFiles)) {
                return new BaseState(false, AppInfo.NOT_ALLOW_FILE_TYPE);
            }

            InputStream inputStream = multipartFile.getInputStream();
            int size = inputStream.available();

            if (size > maxSize) {
                return new BaseState(false, AppInfo.MAX_SIZE);
            }

            //保存文件
            String fileName = UUIDUtil.getUUID() + "." + extName;
            String targetFile = FileUtil.formatPath(uploadDirectory, DateUtil.formatDate(new Date(), DateUtil.YMD_), fileName);
            String physicalPath = DocumentContextUtil.formatPhysicalPath(targetFile);
            FileUtil.writeStreamToFile(physicalPath, inputStream);
            //记录


            //获取图片信息
            Pair<Integer, Integer> widthHeight = ImageUtil.getImageWidthHeight(physicalPath);

            State state = new BaseState(true);
            state.putInfo("width", widthHeight.getLeft());
            state.putInfo("height", widthHeight.getRight());
            state.putInfo("size", size);
            state.putInfo("title", fileName);
            state.putInfo("url", targetFile);
            state.putInfo("type", extName);

            return state;
        } catch (IOException e) {
            log.error("处理文件失败:{}", e.getMessage(), e);
            return new BaseState(false, AppInfo.PARSE_REQUEST_ERROR);
        } catch (Exception e) {
            log.error("处理文件失败:{}", e.getMessage(), e);
        }
        return new BaseState(false, AppInfo.IO_ERROR);
    }

}

这里的参数conf就是上面自定义的配置对象转为map结构

4.前端代码:根据官网demo案例编写自己的编辑器初始化代码,需要吧后端服务地址修改为我们自己的服务地址,至此就能实现集成ueditor并实现配置信息动态获取

editorConfig: {
        UEDITOR_HOME_URL: '/editor/',
        serverUrl: 项目地址 + '/editor?columnId=' + this.columnId
      } 
this.editor = UE.getEditor(this.editorId, this.editorConfig); // 初始化UE

 

posted @ 2023-06-21 18:01  山顶上的蜗牛  阅读(829)  评论(1)    收藏  举报