目录
前戏
博主最近在做的项目中,有一个发布新闻的需求,所以用到了富文本编辑器,经过前端人员的对比分析,最终采用了百度开源的UEditor编辑器。
但是这个事情并不是前端一个部门的事,这是需要前后端配合的,因为涉及到上传图片、文件或附件,一开始踩了很多坑,网友们的意见也是参差不齐,现在终于理清了,完美整合到了项目中,在这里做一下记录,希望帮助到需要的人。
本文章中的示例,采用SSM实现(当然这里没有MyBatis),因为它需要下载UEditor提供的一些JSP版本的文件夹,这些在Spring Boot中配置起来是不划算的,而且我觉得Spring Boot这玩意儿,应该只用来开发分布式或者说微服务的API接口,涉及到页面的东西最好不要采用它。
创建项目
采用maven工具创建,具体的流程不说了,想必各位创建一个maven项目还是不在话下的,只贴一下结构:
其中UEController.java、home.html我们先让它空着,其他文件的内容:
PageController.java:
package com.blog.ueditor.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 页面转发器
* @author 秋枫艳梦
* @date 2019-06-21
* */
@Controller
public class PageController {
/**
* 跳到富文本编辑页面
* @return 视图名称
*
* */
@RequestMapping(value = "/home.html")
public String goHome(){
return "home";
}
}
application-context.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
">
<context:component-scan base-package="com.blog.ueditor.controller"/>
<context:annotation-config />
</beans>
application-servlet.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
">
<import resource="application-context.xml"/> <!--引入另一篇配置文件-->
<mvc:resources mapping="/statics/**" location="/statics/"/>
<!--完成请求与处理器之间的映射,并处理JSON数据格式的转码-->
<mvc:annotation-driven>
<mvc:message-converters>
<!--处理时间格式-->
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8</value>
<value>application/json;charset=UTF-8</value>
</list>
</property>
<property name="features">
<list>
<value>WriteDateUseDateFormat</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- html视图解析器 --> <!-- 必须先配置freemarkerConfig,注意html是没有prefix前缀属性的 -->
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath">
<value>/html/</value>
</property>
<property name="freemarkerSettings">
<props>
<prop key="default_encoding">UTF-8</prop>
</props>
</property>
</bean>
<bean id="htmlviewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="suffix" value=".html"/>
<property name="order" value="0"/>
<property name="contentType" value="text/html;charset=UTF-8"/>
</bean>
<!--配置MultipartResolver,上传文件-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="5000000"/>
<property name="defaultEncoding" value="UTF-8"/>
</bean>
</beans>
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<display-name>Archetype Created Web Application</display-name>
<!--定义核心Servlet,MVC的入口-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application-*.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/home.html</url-pattern>
</servlet-mapping>
<!--定义Spring字节编码过滤器-->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--配置环境参数,指定Spring配置文件所在的位置-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application-*.xml</param-value>
</context-param>
<!--定义监听器,Web容器一启动,就启动Spring容器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<welcome-file-list>
<welcome-file>home.html</welcome-file>
</welcome-file-list>
</web-app>
好了,到这里,我们的环境已经准备好了,接下来让我们下载UE编辑器并进行必要的配置。
下载UEditor的JSP版本
下载完成之后解压出来,是一个utf8-jsp文件夹,复制到项目的根目录,即webapps下:
然后我们把以下jar包添加到自己的pom.xml中:
贴一下最终的pom.xml(以我的为例):
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.blog.ueditor</groupId>
<artifactId>demo</artifactId>
<version>1.0</version>
<packaging>war</packaging>
<name>demo Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context-support -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/jstl -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/taglibs/standard -->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--Spring模块-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-expression -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-instrument -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-messaging -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.3.RELEASE</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--文件模块-->
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.gitee.qdbp.thirdparty/ueditor -->
<dependency>
<groupId>com.gitee.qdbp.thirdparty</groupId>
<artifactId>ueditor</artifactId>
<version>1.4.3.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.json/json -->
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20160810</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
</dependencies>
<build>
<finalName>demo</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
在服务器端进行编码配置
现在我们开始编写UEController.java的代码,先写一个返回配置的方法:
package com.blog.ueditor.controller;
import com.baidu.ueditor.ActionEnter;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* UEditor富文本编辑器,后端控制器
* @author 秋枫艳梦
* @date 2019-06-22
**/
@Controller
@RequestMapping(value = "/ueditor")
public class UEController {
/**
* 设置配置信息,供前端富文本编辑器检验是否可以使用IO流的操作
* @param request 请求体
* @param response 响应体
* */
@RequestMapping(value="/config")
public void config(HttpServletRequest request, HttpServletResponse response) {
response.setContentType("application/json");
String rootPath = request.getSession().getServletContext().getRealPath("/");
PrintWriter writer = null;
try {
String exec = new ActionEnter(request, rootPath).exec();
writer = response.getWriter();
writer.write(exec);
writer.flush();
System.out.println("UEditor富文本编辑器后端配置完毕");
} catch (IOException e) {
e.printStackTrace();
}finally {
writer.close();
}
}
}
在前端页面使用并配置UEditor编辑器
首先在home.html写下如下代码,唤起富文本编辑器(注意看引用的JS脚本):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>UEditor编辑器教程</title>
<!--不要忘了引jQuery-->
<script src="../statics/js/jquery-3.2.1.min.js" type="text/javascript"></script>
<script src="../utf8-jsp/ueditor.config.js"></script>
<script src="../utf8-jsp/ueditor.all.min.js"></script>
<script src="../utf8-jsp/lang/zh-cn/zh-cn.js"></script>
</head>
<body>
<div>
<textarea id="editor" style="width:100%;height:285px;"></textarea>
</div>
</body>
<script>
$(function(){
//富文本编辑器
UE.getEditor('editor');
});
</script>
</html>
然后在utf8-jsp下的ueditor.config.js中做出如下配置:
现在我们先不做别的,运行一下项目,页面上已经有富文本编辑器了,而且后台也打印了消息:
现在我们已经可以欢快的使用了,也支持小表情,标题、段落、字体加粗、样式等功能,可以说已经是一个小型的word文档编辑器了,如果你的项目只是这些需求,那么你已经整合完毕了,接下来的操作可以忽略~~~
实现上传图片的功能(上传附件、文件等同理)
大部分的需求,还是要上传图片或者附件的,甚至视频、其他文件,这里只以上传图片为例,第一是因为这个场景最常见,第二是因为其他的场景跟这个是一样的,只不过做不同的配置,让服务器端不同的处理方法去执行业务即可。
首先我们继续编写UEController.java,增加一个上传图片的处理方法:
/**
* 上传图片
* @param file 上传的图片
* @param request 请求体
* @return Map类型的响应结果
*/
@ResponseBody
@RequestMapping(value = "/images", method = RequestMethod.POST)
public Map<String, Object> images(@RequestParam(value = "imageFile", required = false) MultipartFile file, HttpServletRequest request) {
Map<String, Object> params = new HashMap<String, Object>();
if (file == null) {
System.out.println("文件为空,方法结束");
return null;
}
try {
//为了简便,这里上传到桌面上的static文件夹
String basePath = "C:\\Users\\Administrator\\Desktop\\static";
String ext = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".") + 1).toLowerCase();
String fileName = String.valueOf(System.currentTimeMillis()).concat("_").concat(String.valueOf(Math.random() * 1000) + 1000).concat(".").concat(ext);
StringBuilder sb = new StringBuilder();
//拼接保存路径
sb.append(basePath).append("\\").append(fileName);
String visitUrl = "http://localhost/ue/upload/" + fileName;
File f = new File(sb.toString());
if (!f.exists()) {
f.getParentFile().mkdirs();
}
OutputStream out = new FileOutputStream(f);
FileCopyUtils.copy(file.getInputStream(), out);
params.put("state", "SUCCESS");
params.put("url", visitUrl);
params.put("size", file.getSize());
params.put("original", fileName);
params.put("type", file.getContentType());
params.put("filename", file.getOriginalFilename());
} catch (Exception e) {
params.put("state", "ERROR");
e.printStackTrace();
}
return params;
}
然后,我们打开utf8-jsp下面的ueditor.config.js,做出如下配置:
注意:这里有坑,网上有说直接配置在config.json的,有说两个都要配置的,但博主亲测之后,发现只需要在ueditor.config.js配置即可,config.json可以说压根儿就没什么卵用(当然不能删除),官方文档说是config.json的优先级较低,但是我ueditor.config.js不配置,应该读取config.json啊?实际上配置config.json并不会有效。
其中,imageActionName属性,是action的名字,具体是干嘛用的,待会再说;imageFieldName是上传的图片的名字,即参数名,对应我们后端处理方法的@RequestParam所指定的value属性。
我们继续修改home.html,做出如下修改:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>UEditor编辑器教程</title>
<!--不要忘了引jQuery-->
<script src="../statics/js/jquery-3.2.1.min.js" type="text/javascript"></script>
<script src="../utf8-jsp/ueditor.config.js"></script>
<script src="../utf8-jsp/ueditor.all.min.js"></script>
<script src="../utf8-jsp/lang/zh-cn/zh-cn.js"></script>
</head>
<body>
<div>
<textarea id="editor" style="width:100%;height:285px;"></textarea>
</div>
</body>
<script>
$(function(){
//富文本编辑器
UE.getEditor('editor');
UE.Editor.prototype._bkGetActionUrl = UE.Editor.prototype.getActionUrl;
UE.Editor.prototype.getActionUrl = function(action){
if(action == 'action-image'){
return 'http://localhost/ueditor/images';
}else{
return this._bkGetActionUrl.call(this,action);
}
}
});
</script>
</html>
我们知道,UEditor可以在工具栏指定好多功能,上传图片、附件、文件等待,那它怎样区分这些操作呢?UE的研发团队以不同的action来区分,我们指定了上传图片的action为“action-image”,那么就要在用户使用这个功能时返回一个路径,这个路径就是处理这个action的,这里也就是我们后台上传图片的控制器的路径,这下子理解了吧?
所以,如果你想上传附件、文件、视频等等,在config.json配置对应的模块,然后在home.html做不同的action判断,分发给不同的后端服务器去处理即可。
运行项目,我们可以看到static文件夹下已经有上传的图片了,只是富文本编辑器没有回显图片:
这是为什么呢?F12调试一下,发现富文本框明明有图片路径啊?
这是因为,我们的文件夹写在了tomcat服务器外部,这也是我们经常做的做法,否则服务器目录会愈发臃肿,所以我们需要做一下虚拟路径的映射,在tomcat的server.xml中配置:
另外,在IDEA中调试的话,还需要勾选这一项:
OK,重新运行一下:
已经达到我们想要的效果了,在项目中实际应用的话,再加一个提交事件,获取富文本框的内容,入库,不就完事了???