springboot整合ueditor
这篇文章记录下如何在springboot工程中使用ueditor,没有做前后端分离,ueditor的前端页面也在后端的springboot工程中。使用修改ueditor后端源码码的方式进行整合
一、分析
1.1 使用ueditor为什么需要后端工程的配合
ueditor是一个可以嵌入在前端页面上的富文本编辑器,但它的配置信息和上传图片,上传附件等这些功能的实现需要后端工程的支持,即前端向后端请求配置信息,上传图片。
ueditor官方提供了jsp版的后台代码。这个示例工程可以直接在tomcat中部署进行测试,就是把ueditor前端页面和jsp后端都部署在tomcat中,这种方式官网有详细示例,可以用来熟悉ueditor使用时的前后端交互过程。
1.2 ueditor的jsp版后端代码部分详解
首先给出一个ueditor发给后端的获取配置信息的请求的url示例:
http://localhost:8080/ueditor-demo/jsp/controller.jsp?action=config&&noCache=1589691598506
在官网下载ueditor的源码,其中有一个jsp文件夹,这里边放的就是jsp后端代码
lib目录中是其依赖的jar包,src中是工程源码,config.json是对ueditor进行配置的配置文件,controller.jsp是前端请求的入口。
controller.jsp中的主要内容如下:
request.setCharacterEncoding( "utf-8" );
response.setHeader("Content-Type" , "text/html");
String rootPath = application.getRealPath( "/" );
out.write( new ActionEnter( request, rootPath ).exec() );
可以看到所有请求的处理都是通过ActionEnter这个类中的exec方法处理的。从src目录中找到这个文件。
梳理下这个文件中的主要操作:
(1) 构造方法中实例化了配置文件管理对象ConfigManager,通过这个对象来读取配置文件
public ActionEnter ( HttpServletRequest request, String rootPath ) {
this.request = request;
this.rootPath = rootPath;
this.actionType = request.getParameter( "action" );
this.contextPath = request.getContextPath();
this.configManager = ConfigManager.getInstance( this.rootPath, this.contextPath, request.getRequestURI() );
}
(2) exec方法调用了本类中的invoke方法,在invoke方法中根据前端请求参数action
来判断到底是进行什么操作,这个action在构造方法中通过request获取,如上边的示例url中action=config
(3) invoke方法的内容如下
public String invoke() {
if ( actionType == null || !ActionMap.mapping.containsKey( actionType ) ) {
return new BaseState( false, AppInfo.INVALID_ACTION ).toJSONString();
}
if ( this.configManager == null || !this.configManager.valid() ) {
return new BaseState( false, AppInfo.CONFIG_ERROR ).toJSONString();
}
State state = null;
//1.把前台传递的action转换为actionCode
int actionCode = ActionMap.getType( this.actionType );
Map<String, Object> conf = null;
//2.根据actionCode进行对应的操作:获取配置,上传图片等
switch ( actionCode ) {
case ActionMap.CONFIG:
return this.configManager.getAllConfig().toString();
case ActionMap.UPLOAD_IMAGE:
case ActionMap.UPLOAD_SCRAWL:
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( conf ).capture( list );
break;
case ActionMap.LIST_IMAGE:
case ActionMap.LIST_FILE:
conf = configManager.getConfig( actionCode );
int start = this.getStartIndex();
state = new FileManager( conf ).listFile( start );
break;
}
return state.toJSONString();
}
1.3 总结
在springboot工程中整合ueditor后端部分,我们可以直接拷贝官方提供的jsp后端的src目录中的源码,然后自己建一个Controll来处理来自ueditor的请求,并把所有的请求向jsp中那样使用ActionEnter类的exec方法处理。
二、整合ueditor到springboot
2.1 复制官方提供的源码,导入需要的依赖
把官方提供的jsp文件中的src目录复制到我们自己的工程中,根据根据jsp文件中lib目录下的jar包导入对应的依赖到我们的工程中。
lib目录中的jar包:
对应的导入如下的maven依赖
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20180813</version>
</dependency>
2.2创建一个Controller来处理ueditor的请求
@RestController
@RequestMapping("/ueditor")
public class UEditorController {
@RequestMapping("/config")
public String config(HttpServletRequest request, HttpServletResponse response){
String userDir = System.getProperty("user.dir");
return new ActionEnter(request, userDir).exec();
}
}
这里的参数userDir我指定的是springboot工程打成jar包运行时这个jar包所在的目录,jsp示例中指定的是应用的根路径,这个可根据实际情况调整。
2.4 前端页面构建
在resources目录下新建static文件夹,因为放在这个目录下的内容工程启动时可以被直接访问,所以我们把官方提供的示例直接复制到这个目录下
这样在浏览器中直接访问index.html就可以访问到该页面,修改ueditor.config.js中的serverUrl指向我们定义的这个controller
// 服务器统一请求接口路径
, serverUrl: "ueditor/config"
2.5 配置文件读取位置的调整
现在启动工程已经可以访问到ueditor页面了,但现在因为在后端代码读取配置文件的路径下并没有配置文件,所以前台页面会提示后端配置错误,接下来我们研究下后端代码如何读取配置文件。
前边提到在ActionEnter的构造方法中创建了ConfigManager对象,通过ConfigManager来读取配置文件
ConfigManager的构造方法调用了initEnv方法,这个方法的代码如下
private void initEnv () throws FileNotFoundException, IOException {
File file = new File( this.originalPath );
if ( !file.isAbsolute() ) {
file = new File( file.getAbsolutePath() );
}
this.parentPath = file.getParent();
String configContent = this.readFile( this.getConfigPath() );
try{
JSONObject jsonConfig = new JSONObject( configContent );
this.jsonConfig = jsonConfig;
} catch ( Exception e ) {
this.jsonConfig = null;
}
}
可以看到给readFile方法传入了一个路径返回了configContent,所以是这个readFile方法在读取配置文件,
private String readFile ( String path ) throws IOException {
StringBuilder builder = new StringBuilder();
try {
InputStreamReader reader = new InputStreamReader( new FileInputStream( path ), "UTF-8" );
BufferedReader bfReader = new BufferedReader( reader );
String tmpContent = null;
while ( ( tmpContent = bfReader.readLine() ) != null ) {
builder.append( tmpContent );
}
bfReader.close();
} catch ( UnsupportedEncodingException e ) {
// 忽略
}
return this.filter( builder.toString() );
}
可以看到在这个方法中通过InputStreamReader在读取配置文件。所以我们可以把自己的配置文件放在resources目录下,通过类加载器返回输入流传给InputStreamReader,这样就可以读取到自己的配置文件
private String readFile () throws IOException {
//更改读取后端配置文件的位置
InputStream resourceAsStream = ConfigManager.class.getClassLoader().getResourceAsStream("config.json");
StringBuilder builder = new StringBuilder();
...省略其他
然后这个readFile方法就可以修改为不带参数的
2.6 上传图片未找到数据异常的处理
经过上边的处理后编辑器页面已经可以使用了,但如果上传图片会报未找到数据的异常,这是因为上传图片的请求被springmvc的MultipartResolver
拦截到了并封装成了MultipartFile对象,所以请求中没有文件数据,这个MultipartResolver
是springboot自动配置的StandardServletMultipartResolver
。所以解决的办法是排除掉
springboot自动配置MultipartResolver
,重写一个自己的MultipartResolver
并在其中放行ueditor的上传请求。
(1) 排除springboot自动配置MultipartResolver
在application.yml中进行如下配置
spring:
autoconfigure:
#排除springboot对MultipartResolver的自动配置,使用自己的配置,在其中放行来自ueditor的上传请求
exclude: org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration
(2) 创建一个自定义的MultipartResolver
,继承StandardServletMultipartResolver
public class MyCommonsMultipartResolver extends StandardServletMultipartResolver {
//springmvc对上传文件请求的处理,使其过滤掉ueditor的上传请求
@Override
public boolean isMultipart(HttpServletRequest request) {
String requestURI = request.getRequestURI();
if(requestURI.contains("/ueditor")){
return false;
}
return super.isMultipart(request);
}
}
(3) 把这个自定义类配置到spring容器中
@Bean(name = "multipartResolver")
public MultipartResolver multipartResolver(){
return new MyCommonsMultipartResolver();
}
这样图片上传功能就可以了,图片的保存路径是在创建ActionEnter时的第二个参数指定的路径,再加上config.json中指定的imagePathFormat,可以通过修改配置文件中的这个参数来改变图片的保存路径。
2.7 图片回显问题
上一步只是完成了图片的上传,但图片在编辑器中的回显依然存在问题,报找不到图片的错误,这是因为后端图片上传成功后给前端返回了一个url,前端会访问这个url获取图片。追踪源码会发现在BinaryUploader#save方法中
if (storageState.isSuccess()) {
storageState.putInfo("url", PathFormat.format(savePath));
storageState.putInfo("type", suffix);
storageState.putInfo("original", originFileName + suffix);
}
这个savePath就是上面config.json中的imagePathFormat再加上文件的保存名称而前端拿到这个url后,会直接请求这个地址:http://localhost:8080/url,比如按我的配置文件,这个url是/ueditor/upload/image/20200517/1589695937922096997.png
因为现在工程中不存在这样一个接口,所以获取不到图片。
这里给出一种解决方案,在工程中增加一个获取图片的接口,然后在这里把这个url指向获取图片的接口(文件名拼接在url上),这样前端就可以拿到上传的图片进行回显。
当然也可以修改ueditor的前端回显图片部分直接指向获取图片的接口,这样这里的url就可以只放一个参数名
三、总结
这篇文章记录了在springboot工程中如何整合ueditor,并没有进行前后端分离,导入了ueditor的jsp后端源码并进行了修改,没有修改ueditor的前端源码。