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的前端源碼。