SpringBoot 2 整合富文本编辑器(UEditor)步骤及问题详解

下载UEditor:

下载

环境配置:

项目地址:D:\workspace\project

解压到项目路径下…/src/main/resources/resources/中;
临时转移jsp目录,同时将jsp文件夹中的config.json文件拷贝到下步操作目录下;
项目同级目录下创建文件夹/upload-file/ueditor/(application.yml中配置的上传文件保存地址);

pom引入依赖:


		<dependency>
			<groupId>com.gitee.qdbp.thirdpartygroupId>
			<artifactId>ueditorartifactId>
			<version>1.4.3.3version>
		dependency>

		<dependency>
			<groupId>biz.source_codegroupId>
			<artifactId>base64coderartifactId>
			<version>2010-12-19version>
		dependency>

application.yml配置:

file:
  save:
#    location: ${user.dir}/upload-file  # 自定义的文件保存地址
    location: D:\workspace\project/upload-file  # 自定义的文件保存地址

新建WebMvcConfigurer:

@Configuration
public class WebMvcConfigurer extends WebMvcConfigurerAdapter {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/resources/")
                .addResourceLocations("file:D:\\workspace\\project\\upload-file\\");
        super.addResourceHandlers(registry);


    }
}

新建ServletFilter允许跨域:

/**
 * 全局过滤器,用于处理一些需要在请求前处理的事物
 */
@Component
public class ServletFilter implements Filter {

    /**
     * 该方法用于初始化过滤器
     */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    /**
     * 该方法在容器移除 servlet 时执行,同样只执行一次
     */
    @Override
    public void destroy() {}

    /**
     * 该方法只执行在响应 WEB 请求的最开始
     * 这里添加了任意域名任意请求方式可跨域访问的功能
     * Access-Control-Allow-Origin   允许跨域访问的域名
     * Access-Control-Allow-Headers  允许任何请求头访问
     * Access-Control-Allow-Methods  允许跨域访问的请求方式
     */
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Headers", "*");
        chain.doFilter(req, res);
    }
}

新建UeditorController:

@RestController
@RequestMapping("ueditor")
public class UeditorController {

    // 读取配置文件中配置的上传文件保存地址
    @Value("${file.save.location}")
    private String ueditorUrl;

    /**
     * 接收和获取百度编辑插件的文件,需要注意以下几点
     * 1,需要在 ueditorUrl 目录下创建 ueditor 目录,将 config.json 拷贝到该路径下
     * 2,必须将 ueditorUrl 目录设置为静态资源路径,否则上传的文件可能无法访问
     * 3,注意访问配置文件的方式 ueditor.config.js 请求的路径就是 config.json 放置的同级路径
     */
    @RequestMapping("upload")
    public String upload(HttpServletRequest request, String action) {
        String result = new ActionEnter(request, ueditorUrl).exec();
        ueditorUrl = ueditorUrl.replaceAll("\\\\", "/");
        if (action != null && (action.equals("listfile") || action.equals("listimage"))) {
            return result.replaceAll(ueditorUrl, "");
        }
        return result;
    }

}

修改config.json增加资源访问前缀:

…Prefix:“项目地址”

/* 前后端通信相关的配置,注释只允许使用多行方式 */
{
    /* 上传图片配置项 */
    "imageActionName": "uploadimage", /* 执行上传图片的action名称 */
    "imageFieldName": "files", /* 提交的图片表单名称 */
    "imageMaxSize": 2048000, /* 上传大小限制,单位B */
    "imageAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 上传图片格式显示 */
    "imageCompressEnable": true, /* 是否压缩图片,默认是true */
    "imageCompressBorder": 1600, /* 图片压缩最长边限制 */
    "imageInsertAlign": "none", /* 插入的图片浮动方式 */
    "imageUrlPrefix": "http://localhost", /* 图片访问路径前缀 */
    "imagePathFormat": "/ueditor/jsp/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
                                /* {filename} 会替换成原文件名,配置这项需要注意中文乱码问题 */
                                /* {rand:6} 会替换成随机数,后面的数字是随机数的位数 */
                                /* {time} 会替换成时间戳 */
                                /* {yyyy} 会替换成四位年份 */
                                /* {yy} 会替换成两位年份 */
                                /* {mm} 会替换成两位月份 */
                                /* {dd} 会替换成两位日期 */
                                /* {hh} 会替换成两位小时 */
                                /* {ii} 会替换成两位分钟 */
                                /* {ss} 会替换成两位秒 */
                                /* 非法字符 \ : * ? " < > | */
                                /* 具请体看线上文档: fex.baidu.com/ueditor/#use-format_upload_filename */

    /* 涂鸦图片上传配置项 */
    "scrawlActionName": "uploadscrawl", /* 执行上传涂鸦的action名称 */
    "scrawlFieldName": "upfile", /* 提交的图片表单名称 */
    "scrawlPathFormat": "/ueditor/jsp/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
    "scrawlMaxSize": 2048000, /* 上传大小限制,单位B */
    "scrawlUrlPrefix": "http://localhost", /* 图片访问路径前缀 */
    "scrawlInsertAlign": "none",

    /* 截图工具上传 */
    "snapscreenActionName": "uploadimage", /* 执行上传截图的action名称 */
    "snapscreenPathFormat": "/ueditor/jsp/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
    "snapscreenUrlPrefix": "http://localhost", /* 图片访问路径前缀 */
    "snapscreenInsertAlign": "none", /* 插入的图片浮动方式 */

    /* 抓取远程图片配置 */
    "catcherLocalDomain": ["127.0.0.1", "localhost", "img.baidu.com"],
    "catcherActionName": "catchimage", /* 执行抓取远程图片的action名称 */
    "catcherFieldName": "source", /* 提交的图片列表表单名称 */
    "catcherPathFormat": "/ueditor/jsp/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
    "catcherUrlPrefix": "http://localhost", /* 图片访问路径前缀 */
    "catcherMaxSize": 2048000, /* 上传大小限制,单位B */
    "catcherAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 抓取图片格式显示 */

    /* 上传视频配置 */
    "videoActionName": "uploadvideo", /* 执行上传视频的action名称 */
    "videoFieldName": "upfile", /* 提交的视频表单名称 */
    "videoPathFormat": "/ueditor/jsp/upload/video/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
    "videoUrlPrefix": "http://localhost", /* 视频访问路径前缀 */
    "videoMaxSize": 102400000, /* 上传大小限制,单位B,默认100MB */
    "videoAllowFiles": [
        ".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg",
        ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid"], /* 上传视频格式显示 */

    /* 上传文件配置 */
    "fileActionName": "uploadfile", /* controller里,执行上传视频的action名称 */
    "fileFieldName": "upfile", /* 提交的文件表单名称 */
    "filePathFormat": "/ueditor/jsp/upload/file/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
    "fileUrlPrefix": "http://localhost", /* 文件访问路径前缀 */
    "fileMaxSize": 51200000, /* 上传大小限制,单位B,默认50MB */
    "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", /* 执行图片管理的action名称 */
    "imageManagerListPath": "/ueditor/jsp/upload/image/", /* 指定要列出图片的目录 */
    "imageManagerListSize": 20, /* 每次列出文件数量 */
    "imageManagerUrlPrefix": "http://localhost", /* 图片访问路径前缀 */
    "imageManagerInsertAlign": "none", /* 插入的图片浮动方式 */
    "imageManagerAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 列出的文件类型 */

    /* 列出指定目录下的文件 */
    "fileManagerActionName": "listfile", /* 执行文件管理的action名称 */
    "fileManagerListPath": "/ueditor/jsp/upload/file/", /* 指定要列出文件的目录 */
    "fileManagerUrlPrefix": "http://localhost", /* 文件访问路径前缀 */
    "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"
    ] /* 列出的文件类型 */

}

修改ueditor.config.js:

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

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

        // 服务器统一请求接口路径
        // , serverUrl: URL + "jsp/controller.jsp"
		// , serverUrl: URL + "servlet"
		, serverUrl: "http://localhost:8080/ueditor/upload"
		

访问index.html:

解决图片上传问题:

修改ueditor.all.js:

            var form = btnIframeDoc.getElementById('edui_form_' + timestrap);
            var input = btnIframeDoc.getElementById('edui_input_' + timestrap);
            var iframe = btnIframeDoc.getElementById('edui_iframe_' + timestrap);

            // ------------------------------代码修改区开始----------------------------
            domUtils.on(input, 'change', function(){
                if(!input.value) return;
                var loadingId = 'loading_' + (+new Date()).toString(36);
                var params = utils.serializeParam(me.queryCommandValue('serverparam')) || '';
                var imageActionUrl = me.getActionUrl(me.getOpt('imageActionName'));
                var allowFiles = me.getOpt('imageAllowFiles');
                me.focus();
                me.execCommand('inserthtml', '+ loadingId + '" src="' + me.options.themePath + me.options.theme +'/images/spacer.gif" title="' + (me.getLang('simpleupload.loading') || '') + '" >');
                var filename = input.value,
                    fileext = filename ? filename.substr(filename.lastIndexOf('.')) : '';
                if (!fileext || (allowFiles && (allowFiles.join('') + '.').indexOf(fileext.toLowerCase() + '.') == -1)) {
                    showErrorLoader(me.getLang('simpleupload.exceedTypeError'));
                    return;
                }
                var params = utils.serializeParam(me.queryCommandValue('serverparam')) || '';
                var action = utils.formatUrl(imageActionUrl + (imageActionUrl.indexOf('?') == -1 ? '?' : '&') + params);
                var formData = new FormData();
                formData.append("upfile", form[0].files[0]);

                var xhr = new XMLHttpRequest();
                xhr.open("post", action, true);
                xhr.onload = function () {
                    data = eval("(" + xhr.response + ")");
                    var link, loader,
                        body = (iframe.contentDocument || iframe.contentWindow.document).body,
                        result = body.innerText || body.textContent || '';
                    link = me.options.imageUrlPrefix + data.url;

                    if (data.state == 'SUCCESS' && data.url) {
                        loader = me.document.getElementById(loadingId);
                        loader.setAttribute('src', link);
                        loader.setAttribute('_src', link);
                        loader.setAttribute('title', data.title || '');
                        loader.setAttribute('alt', data.original || '');
                        loader.removeAttribute('id');
                        domUtils.removeClasses(loader, 'loadingclass');
                    } else {
                        showErrorLoader && showErrorLoader(data.state);
                    }
                    form.reset();
                }
                xhr.send(formData);

                function showErrorLoader(title){
                    if(loadingId) {
                        var loader = me.document.getElementById(loadingId);
                        loader && domUtils.remove(loader);
                        me.fireEvent('showmessage', {
                            'id': loadingId,
                            'content': title,
                            'type': 'error',
                            'timeout': 4000
                        });
                    }
                }
            });
            var stateTimer;
            me.addListener('selectionchange', function () {
                clearTimeout(stateTimer);
                stateTimer = setTimeout(function() {
                    var state = me.queryCommandState('simpleupload');
                    if (state == -1) {
                        input.disabled = 'disabled';
                    } else {
                        input.disabled = false;
                    }
                }, 400);
            });
        });
        // ------------------------------代码修改区结束----------------------------

        btnIframe.style.cssText = btnStyle;
        containerBtn.appendChild(btnIframe);
    }

修改index.html相关引用代码

新建UploadController:

@RestController
public class UploadController {

    @RequestMapping("/upload")
    public Object upload(@RequestParam(value = "files") MultipartFile files, HttpServletRequest req) {
        String rootPath = req.getServletContext().getRealPath("/");
        System.out.println(rootPath);
        UeditorResponse ur = new UeditorResponse();
        ur.setType(files.getOriginalFilename());
        ur.setOriginal(files.getOriginalFilename());
        try {
            System.out.println(files.getName());
            System.out.println(files.getOriginalFilename());
            String path = "D:/" + files.getOriginalFilename();
            files.transferTo(new File(path));
            ur.setUrl(path);
            ur.setState("SUCCESS");
        } catch (IOException e) {
            e.printStackTrace();
            ur.setState("图片上传失败");
            return ur;
        }
        return ur;
    }

    @RequestMapping("/uploadbase64")
    public Object uploadbase64(String files) {
        System.out.println(files);
        UeditorResponse ur = new UeditorResponse();
        savebase64(files, "D:/xxx.png");
        ur.setState("SUCCESS");
        ur.setUrl("url");
        ur.setOriginal("xxx.png");
        return ur;
    }

    //保存base64为图片
    public Object savebase64(String file, String path) {
        file = file.substring(file.indexOf(",") + 1);
        System.out.println(file);
        try {
            Base64.Decoder decoder = Base64.getDecoder();
            byte[] bytes = decoder.decode(file);
            for (int i = 0; i < bytes.length; ++i) {
                if (bytes[i] < 0) {// 调整异常数据
                    bytes[i] += 256;
                }
            }
            InputStream in = new ByteArrayInputStream(bytes);
            FileOutputStream fos = new FileOutputStream(new File(path));
            byte[] b = new byte[4096];
            Integer len = -1;
            while ((len = in.read(b)) != -1) {
                fos.write(b, 0, len);
            }
            fos.flush();
            in.close();
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

新建UeditorResponse:

@Data
public class UeditorResponse {
    //图片上传成功后返回获取图片的路径,
    //可以是一个二进制流的接口,也可以是图片地址得接口,总之这个路径必须是写在img标签内能够显示的
    private String url;
    //上传状态信息,如果为"SUCCESS"(必须是大写),则表示上传成功,不为SUCCESS时页面提示信息就是state得值
    private String state;
    private String type;
    private String original;
}

新建MyConfigManager:

public final class MyConfigManager {
    private final Logger logger = LoggerFactory.getLogger(MyConfigManager.class);

    private JSONObject jsonConfig = null;
    private static MyConfigManager manager;

    private MyConfigManager() throws IOException {
        this.initEnv();
    }

    public static MyConfigManager getInstance() {
        if (manager != null) {
            //ueditor每次实例化都会获取配置文件,单列模式,防止一直new对象
            return manager;
        }
        try {
            manager = new MyConfigManager();
            return manager;
        } catch (Exception var4) {
            return null;
        }
    }

    public boolean valid() {
        return this.jsonConfig != null;
    }

    public JSONObject getAllConfig() {
        if (this.jsonConfig != null) {
            return this.jsonConfig;
        } else {
            JSONObject state=new JSONObject();
            //将“配置文件初始化失败”转成unicode编码返回
            state.put("state","\\u914d\\u7f6e\\u6587\\u4ef6\\u521d\\u59cb\\u5316\\u5931\\u8d25");
            return state;
        }
    }

    private void initEnv() throws IOException {
        String configContent = this.readFile();
        try {
            JSONObject e = JSONObject.parseObject(configContent);
            this.jsonConfig = e;
        } catch (Exception var4) {
            this.jsonConfig = null;
        }

    }

    private String readFile() throws IOException {
        StringBuilder builder = new StringBuilder();
        logger.info("开始读取文件:");
        ClassPathResource resource = new ClassPathResource("config.json");
        try {
            InputStreamReader reader = new InputStreamReader(resource.getInputStream(), "UTF-8");
            BufferedReader bfReader = new BufferedReader(reader);
            String tmpContent = null;

            while ((tmpContent = bfReader.readLine()) != null) {
                builder.append(tmpContent);
            }
            logger.info("文件读取完成:");
            bfReader.close();
        } catch (UnsupportedEncodingException var6) {
            logger.info("文件读取出错:");

        }
        return this.filter(builder.toString());
    }

    private String filter(String input) {
        return input.replaceAll("/\\*[\\s\\S]*?\\*/", "");
    }
}

问题:

本地测试图片显示等可能会有端口问题,根据自身项目情况排查

参考:

springboot2 整合 ueditor 富文本编辑器 并实现前后端分离
ueditor的使用

你可能感兴趣的:(Java笔记)