springboot+thymeleaf集成ueditor

UEditor只提供JSP版本的後端入口代碼。但提供了項目源碼,因此可以根據業務需求修改源代碼。

此處使用了SpringBoot框架,配備了Thymeleaf模板引擎,所以沒有必要再添加jsp來兼容UEditor,可通過修改源碼滿足需要。下面是詳細教程。

1.新建SpringBoot項目,添加web和thymeleaf包

pom文件如下:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <groupId>com.example</groupId>
  6. <artifactId>ueditor-test</artifactId>
  7. <version>0.0.1-SNAPSHOT</version>
  8. <packaging>jar</packaging>
  9. <name>ueditor-test</name>
  10. <description>Demo project for Spring Boot</description>
  11. <parent>
  12. <groupId>org.springframework.boot</groupId>
  13. <artifactId>spring-boot-starter-parent</artifactId>
  14. <version>1.5.2.RELEASE</version>
  15. <relativePath/> <!-- lookup parent from repository -->
  16. </parent>
  17. <properties>
  18. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  19. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  20. <java.version>1.8</java.version>
  21. <!--修改thymeleaf版本-->
  22. <thymeleaf.version>3.0.3.RELEASE</thymeleaf.version>
  23. <thymeleaf-layout-dialect.version>2.1.0</thymeleaf-layout-dialect.version>
  24. </properties>
  25. <dependencies>
  26. <dependency>
  27. <groupId>org.springframework.boot</groupId>
  28. <artifactId>spring-boot-starter-thymeleaf</artifactId>
  29. </dependency>
  30. <dependency>
  31. <groupId>org.springframework.boot</groupId>
  32. <artifactId>spring-boot-starter-web</artifactId>
  33. </dependency>
  34. <dependency>
  35. <groupId>org.springframework.boot</groupId>
  36. <artifactId>spring-boot-starter-test</artifactId>
  37. <scope>test</scope>
  38. </dependency>
  39. </dependencies>
  40. <build>
  41. <plugins>
  42. <plugin>
  43. <groupId>org.springframework.boot</groupId>
  44. <artifactId>spring-boot-maven-plugin</artifactId>
  45. </plugin>
  46. </plugins>
  47. </build>
  48. </project>

2.從官網下載源代碼並解壓至項目,注意config.json我拷到了resources根路徑下,如圖:



3.添加UEditorController,跳轉到index頁面:

  1. package com.example;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. /**
  5. * Created by ldb on 2017/4/9.
  6. */
  7. @Controller
  8. public class UEditorController {
  9. @RequestMapping("/")
  10. private String showPage(){
  11. return "index";
  12. }
  13. }

4.運行項目。訪問路徑localhost:8080,跳轉到如下界面即是源碼已拷貝成功



5.此時發現上傳圖片功能不能用。下面接着看。修改pom,添加UEditor依賴的Jar包。pom文件如下: 

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <groupId>com.example</groupId>
  6. <artifactId>ueditor</artifactId>
  7. <version>0.0.1-SNAPSHOT</version>
  8. <packaging>jar</packaging>
  9. <name>ueditor</name>
  10. <description>Demo project for Spring Boot</description>
  11. <parent>
  12. <groupId>org.springframework.boot</groupId>
  13. <artifactId>spring-boot-starter-parent</artifactId>
  14. <version>1.5.2.RELEASE</version>
  15. <relativePath/> <!-- lookup parent from repository -->
  16. </parent>
  17. <properties>
  18. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  19. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  20. <java.version>1.8</java.version>
  21. <thymeleaf.version>3.0.3.RELEASE</thymeleaf.version>
  22. <thymeleaf-layout-dialect.version>2.1.0</thymeleaf-layout-dialect.version>
  23. </properties>
  24. <dependencies>
  25. <dependency>
  26. <groupId>org.springframework.boot</groupId>
  27. <artifactId>spring-boot-starter-thymeleaf</artifactId>
  28. </dependency>
  29. <dependency>
  30. <groupId>org.springframework.boot</groupId>
  31. <artifactId>spring-boot-starter-web</artifactId>
  32. </dependency>
  33. <dependency>
  34. <groupId>org.springframework.boot</groupId>
  35. <artifactId>spring-boot-starter-test</artifactId>
  36. <scope>test</scope>
  37. </dependency>
  38. <!--UEditor依賴的jar包 -->
  39. <dependency>
  40. <groupId>org.json</groupId>
  41. <artifactId>json</artifactId>
  42. </dependency>
  43. <dependency>
  44. <groupId>commons-fileupload</groupId>
  45. <artifactId>commons-fileupload</artifactId>
  46. <version>1.3.2</version>
  47. </dependency>
  48. <dependency>
  49. <groupId>commons-codec</groupId>
  50. <artifactId>commons-codec</artifactId>
  51. <version>1.9</version>
  52. </dependency>
  53. </dependencies>
  54. <build>
  55. <plugins>
  56. <plugin>
  57. <groupId>org.springframework.boot</groupId>
  58. <artifactId>spring-boot-maven-plugin</artifactId>
  59. </plugin>
  60. </plugins>
  61. </build>
  62. </project>
6.照着源碼裏的controller.jsp.依樣畫葫蘆,寫入UEditorController類,映射路徑爲config。
  1. package com.example;
  2. import com.baidu.ueditor.ActionEnter;
  3. import org.springframework.stereotype.Controller;
  4. import org.springframework.web.bind.annotation.RequestMapping;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7. import java.io.IOException;
  8. import java.io.PrintWriter;
  9. /**
  10. * Created by ldb on 2017/4/9.
  11. */
  12. @Controller
  13. public class UEditorController {
  14. @RequestMapping("/")
  15. private String showPage(){
  16. return "index";
  17. }
  18. @RequestMapping(value="/config")
  19. public void config(HttpServletRequest request, HttpServletResponse response) {
  20. response.setContentType("application/json");
  21. String rootPath = request.getSession().getServletContext().getRealPath("/");
  22. try {
  23. String exec = new ActionEnter(request, rootPath).exec();
  24. PrintWriter writer = response.getWriter();
  25. writer.write(exec);
  26. writer.flush();
  27. writer.close();
  28. } catch (IOException e) {
  29. e.printStackTrace();
  30. }
  31. }
  32. }
7.一步一步debug,發現無法加載config.json文件。此時修改ConfigManage類的getConfigPath()方法。如下:

  1. package com.baidu.ueditor;
  2. import com.baidu.ueditor.define.ActionMap;
  3. import org.json.JSONArray;
  4. import org.json.JSONObject;
  5. import java.io.*;
  6. import java.net.URISyntaxException;
  7. import java.util.HashMap;
  8. import java.util.Map;
  9. /**
  10. * 配置管理器
  11. *
  12. */
  13. public final class ConfigManager {
  14. private final String rootPath;
  15. private final String originalPath;
  16. private final String contextPath;
  17. private static final String configFileName = "config.json";
  18. private String parentPath = null;
  19. private JSONObject jsonConfig = null;
  20. // 塗鴉上傳filename定義
  21. private final static String SCRAWL_FILE_NAME = "scrawl";
  22. // 遠程圖片抓取filename定義
  23. private final static String REMOTE_FILE_NAME = "remote";
  24. /*
  25. * 通過一個給定的路徑構建一個配置管理器, 該管理器要求地址路徑所在目錄下必須存在config.properties文件
  26. */
  27. private ConfigManager ( String rootPath, String contextPath, String uri ) throws FileNotFoundException, IOException {
  28. rootPath = rootPath.replace( "\\", "/" );
  29. this.rootPath = rootPath;
  30. this.contextPath = contextPath;
  31. if ( contextPath.length() > 0 ) {
  32. this.originalPath = this.rootPath + uri.substring( contextPath.length() );
  33. } else {
  34. this.originalPath = this.rootPath + uri;
  35. }
  36. this.initEnv();
  37. }
  38. /**
  39. * 配置管理器構造工廠
  40. * @param rootPath 服務器根路徑
  41. * @param contextPath 服務器所在項目路徑
  42. * @param uri 當前訪問的uri
  43. * @return 配置管理器實例或者null
  44. */
  45. public static ConfigManager getInstance ( String rootPath, String contextPath, String uri ) {
  46. try {
  47. return new ConfigManager(rootPath, contextPath, uri);
  48. } catch ( Exception e ) {
  49. return null;
  50. }
  51. }
  52. // 驗證配置文件加載是否正確
  53. public boolean valid () {
  54. return this.jsonConfig != null;
  55. }
  56. public JSONObject getAllConfig () {
  57. return this.jsonConfig;
  58. }
  59. public Map<String, Object> getConfig ( int type ) {
  60. Map<String, Object> conf = new HashMap<String, Object>();
  61. String savePath = null;
  62. switch ( type ) {
  63. case ActionMap.UPLOAD_FILE:
  64. conf.put( "isBase64", "false" );
  65. conf.put( "maxSize", this.jsonConfig.getLong( "fileMaxSize" ) );
  66. conf.put( "allowFiles", this.getArray( "fileAllowFiles" ) );
  67. conf.put( "fieldName", this.jsonConfig.getString( "fileFieldName" ) );
  68. savePath = this.jsonConfig.getString( "filePathFormat" );
  69. break;
  70. case ActionMap.UPLOAD_IMAGE:
  71. conf.put( "isBase64", "false" );
  72. conf.put( "maxSize", this.jsonConfig.getLong( "imageMaxSize" ) );
  73. conf.put( "allowFiles", this.getArray( "imageAllowFiles" ) );
  74. conf.put( "fieldName", this.jsonConfig.getString( "imageFieldName" ) );
  75. savePath = this.jsonConfig.getString( "imagePathFormat" );
  76. break;
  77. case ActionMap.UPLOAD_VIDEO:
  78. conf.put( "maxSize", this.jsonConfig.getLong( "videoMaxSize" ) );
  79. conf.put( "allowFiles", this.getArray( "videoAllowFiles" ) );
  80. conf.put( "fieldName", this.jsonConfig.getString( "videoFieldName" ) );
  81. savePath = this.jsonConfig.getString( "videoPathFormat" );
  82. break;
  83. case ActionMap.UPLOAD_SCRAWL:
  84. conf.put( "filename", ConfigManager.SCRAWL_FILE_NAME );
  85. conf.put( "maxSize", this.jsonConfig.getLong( "scrawlMaxSize" ) );
  86. conf.put( "fieldName", this.jsonConfig.getString( "scrawlFieldName" ) );
  87. conf.put( "isBase64", "true" );
  88. savePath = this.jsonConfig.getString( "scrawlPathFormat" );
  89. break;
  90. case ActionMap.CATCH_IMAGE:
  91. conf.put( "filename", ConfigManager.REMOTE_FILE_NAME );
  92. conf.put( "filter", this.getArray( "catcherLocalDomain" ) );
  93. conf.put( "maxSize", this.jsonConfig.getLong( "catcherMaxSize" ) );
  94. conf.put( "allowFiles", this.getArray( "catcherAllowFiles" ) );
  95. conf.put( "fieldName", this.jsonConfig.getString( "catcherFieldName" ) + "[]" );
  96. savePath = this.jsonConfig.getString( "catcherPathFormat" );
  97. break;
  98. case ActionMap.LIST_IMAGE:
  99. conf.put( "allowFiles", this.getArray( "imageManagerAllowFiles" ) );
  100. conf.put( "dir", this.jsonConfig.getString( "imageManagerListPath" ) );
  101. conf.put( "count", this.jsonConfig.getInt( "imageManagerListSize" ) );
  102. break;
  103. case ActionMap.LIST_FILE:
  104. conf.put( "allowFiles", this.getArray( "fileManagerAllowFiles" ) );
  105. conf.put( "dir", this.jsonConfig.getString( "fileManagerListPath" ) );
  106. conf.put( "count", this.jsonConfig.getInt( "fileManagerListSize" ) );
  107. break;
  108. }
  109. conf.put( "savePath", savePath );
  110. conf.put( "rootPath", this.rootPath );
  111. return conf;
  112. }
  113. private void initEnv () throws FileNotFoundException, IOException {
  114. File file = new File( this.originalPath );
  115. if ( !file.isAbsolute() ) {
  116. file = new File( file.getAbsolutePath() );
  117. }
  118. this.parentPath = file.getParent();
  119. String configContent = this.readFile( this.getConfigPath() );
  120. try{
  121. JSONObject jsonConfig = new JSONObject( configContent );
  122. this.jsonConfig = jsonConfig;
  123. } catch ( Exception e ) {
  124. this.jsonConfig = null;
  125. }
  126. }
  127. private String getConfigPath () {
  128. //return this.parentPath + File.separator + ConfigManager.configFileName;
  129. try {
  130. //獲取classpath下的config.json路徑
  131. return this.getClass().getClassLoader().getResource("config.json").toURI().getPath();
  132. } catch (URISyntaxException e) {
  133. return null;
  134. }
  135. }
  136. private String[] getArray ( String key ) {
  137. JSONArray jsonArray = this.jsonConfig.getJSONArray( key );
  138. String[] result = new String[ jsonArray.length() ];
  139. for ( int i = 0, len = jsonArray.length(); i < len; i++ ) {
  140. result[i] = jsonArray.getString( i );
  141. }
  142. return result;
  143. }
  144. private String readFile ( String path ) throws IOException {
  145. StringBuilder builder = new StringBuilder();
  146. try {
  147. InputStreamReader reader = new InputStreamReader( new FileInputStream( path ), "UTF-8" );
  148. BufferedReader bfReader = new BufferedReader( reader );
  149. String tmpContent = null;
  150. while ( ( tmpContent = bfReader.readLine() ) != null ) {
  151. builder.append( tmpContent );
  152. }
  153. bfReader.close();
  154. } catch ( UnsupportedEncodingException e ) {
  155. // 忽略
  156. }
  157. return this.filter( builder.toString() );
  158. }
  159. // 過濾輸入字符串, 剔除多行註釋以及替換掉反斜槓
  160. private String filter ( String input ) {
  161. return input.replaceAll( "/\\*[\\s\\S]*?\\*/", "" );
  162. }
  163. }
this.getClass().getClassLoader().getResource("config.json").toURI().getPath(); 
此處需要先轉爲URI再getPath(),否則如果你的項目路徑帶空格或者帶中文則無法讀取到文件

8.運行項目路徑http://localhost:8080/config?action=config,如下圖顯示則表示可讀取到config.json文件



9.此時點擊上傳圖片顯示 如下


提示未找到上傳數據。繼續一步步debug,發現在BinaryUploader類竟然無法獲取到字節流


google得到原因是因爲SpringMVC框架對含字節流的request進行了處理,此處傳的是處理過的request,故獲取不到字節流。此時採用SpringMVC框架的解析器multipartResolver。修改源碼如下:

  1. package com.baidu.ueditor.upload;
  2. import com.baidu.ueditor.PathFormat;
  3. import com.baidu.ueditor.define.AppInfo;
  4. import com.baidu.ueditor.define.BaseState;
  5. import com.baidu.ueditor.define.FileType;
  6. import com.baidu.ueditor.define.State;
  7. import org.apache.commons.fileupload.servlet.ServletFileUpload;
  8. import org.springframework.web.multipart.MultipartFile;
  9. import org.springframework.web.multipart.MultipartHttpServletRequest;
  10. import javax.servlet.http.HttpServletRequest;
  11. import java.io.IOException;
  12. import java.io.InputStream;
  13. import java.util.Arrays;
  14. import java.util.List;
  15. import java.util.Map;
  16. public class BinaryUploader {
  17. public static final State save(HttpServletRequest request,
  18. Map<String, Object> conf) {
  19. // FileItemStream fileStream = null;
  20. // boolean isAjaxUpload = request.getHeader( "X_Requested_With" ) != null;
  21. if (!ServletFileUpload.isMultipartContent(request)) {
  22. return new BaseState(false, AppInfo.NOT_MULTIPART_CONTENT);
  23. }
  24. // ServletFileUpload upload = new ServletFileUpload(
  25. // new DiskFileItemFactory());
  26. //
  27. // if ( isAjaxUpload ) {
  28. // upload.setHeaderEncoding( "UTF-8" );
  29. // }
  30. try {
  31. // FileItemIterator iterator = upload.getItemIterator(request);
  32. //
  33. // while (iterator.hasNext()) {
  34. // fileStream = iterator.next();
  35. //
  36. // if (!fileStream.isFormField())
  37. // break;
  38. // fileStream = null;
  39. // }
  40. //
  41. // if (fileStream == null) {
  42. // return new BaseState(false, AppInfo.NOTFOUND_UPLOAD_DATA);
  43. // }
  44. MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
  45. MultipartFile multipartFile = multipartRequest.getFile(conf.get("fieldName").toString());
  46. if(multipartFile==null){
  47. return new BaseState(false, AppInfo.NOTFOUND_UPLOAD_DATA);
  48. }
  49. String savePath = (String) conf.get("savePath");
  50. //String originFileName = fileStream.getName();
  51. String originFileName = multipartFile.getOriginalFilename();
  52. String suffix = FileType.getSuffixByFilename(originFileName);
  53. originFileName = originFileName.substring(0,
  54. originFileName.length() - suffix.length());
  55. savePath = savePath + suffix;
  56. long maxSize = ((Long) conf.get("maxSize")).longValue();
  57. if (!validType(suffix, (String[]) conf.get("allowFiles"))) {
  58. return new BaseState(false, AppInfo.NOT_ALLOW_FILE_TYPE);
  59. }
  60. savePath = PathFormat.parse(savePath, originFileName);
  61. String physicalPath = (String) conf.get("rootPath") + savePath;
  62. //InputStream is = fileStream.openStream();
  63. InputStream is = multipartFile.getInputStream();
  64. State storageState = StorageManager.saveFileByInputStream(is,
  65. physicalPath, maxSize);
  66. is.close();
  67. if (storageState.isSuccess()) {
  68. storageState.putInfo("url", PathFormat.format(savePath));
  69. storageState.putInfo("type", suffix);
  70. storageState.putInfo("original", originFileName + suffix);
  71. }
  72. return storageState;
  73. // } catch (FileUploadException e) {
  74. // return new BaseState(false, AppInfo.PARSE_REQUEST_ERROR);
  75. } catch (IOException e) {
  76. }
  77. return new BaseState(false, AppInfo.IO_ERROR);
  78. }
  79. private static boolean validType(String type, String[] allowTypes) {
  80. List<String> list = Arrays.asList(allowTypes);
  81. return list.contains(type);
  82. }
  83. }

此時進行上傳圖片,已經能夠成功上傳了。



10.可是圖片究竟上傳到哪裏了呢?繼續一步步debug發現,上傳到如圖路徑



如圖路徑爲tomcat緩存路徑,只要重啓下tomcat該文件就會被刪除。我們需要將其存儲到磁盤中。此時修改config.json文件。



紅色箭頭爲修改處。我需要將文件存儲到E:/image/**下,此處我多添加了basePath,是想把視頻、音樂等靜態資源都存儲到E盤。由於添加了basePath,需要修改配置。通過debug來到ConfigManage


添加紅色箭頭代碼,將basePath塞進配置文件裏。之後繼續來到上傳文件類BinaryUploader,修改如下代碼:



運行項目,點擊添加圖片。打開E盤的image目錄,如圖,成功上傳到E盤對應路徑


11.打開瀏覽器,發現頁面無法加載圖片。如下圖:


打開瀏覽器調試器。如圖


無法獲取到圖片。這是當然的,因爲我們把圖片存在E盤了,而spring並沒有對E盤目錄進行映射。此時我們加入路徑映射。打開application.properties文件,添加如下代碼

web.upload-path=E:/
spring.mvc.static-path-pattern=/**
spring.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,file:${web.upload-path}

此時重新運行項目,點擊上傳圖片,圖片已經能夠正常顯示了。



12.至此,SpringBoot整合UEditor應該完了吧。別急,SpringBoot主張打包成Jar包運行,我們用Maven來打包運行試


java -jar 

打開項目地址,點擊上傳圖片,發現竟然上傳不了了??!!



這是怎麼回事呢?爲什麼打成Jar包後就無法上傳圖片了呢。經過不斷的debug和google。。發現了在Jar包裏無法以ClassLoader.getResource().getPath()獲得的路徑讀取文件,得用Class類的getResourceAsStream()來讀取。具體博文如下:

http://hxraid.iteye.com/blog/483115?page=3#comments

13.那麼我們就來修改源碼,改成getResourceAsStream讀取config.json文件吧。打開ConfigManager類,修改initEnv方法

  1. private void initEnv () throws FileNotFoundException, IOException {
  2. File file = new File( this.originalPath );
  3. if ( !file.isAbsolute() ) {
  4. file = new File( file.getAbsolutePath() );
  5. }
  6. this.parentPath = file.getParent();
  7. //String configContent = this.readFile( this.getConfigPath() );
  8. String configContent = this.filter(IOUtils.toString(this.getClass().getClassLoader().getResourceAsStream("config.json")));
  9. try{
  10. JSONObject jsonConfig = new JSONObject( configContent );
  11. this.jsonConfig = jsonConfig;
  12. } catch ( Exception e ) {
  13. this.jsonConfig = null;
  14. }
  15. }

14. ok了,再次打包,運行項目


成功了!!!

項目源碼:鏈接:https://pan.baidu.com/s/15o4qhHCIsgRQBQ0urzhSGg 提取碼:8mzl

本次教程到此結束。謝謝大家

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章