UEditor富文字編輯器整合經驗分享(整合至SpringMVC)
一、引言
前段時間開發郵件模組需要使用到一款富文字編輯器,綜合考察之後選擇了 UEditor——百度的富文字編輯器。在整合到專案的過程中,遇到了不少問題,困擾了不少時間,在整合過程中也參考了不少前輩們的文章收穫良多,本文主要是談談自己在整合過程中遇到各種問題(或者網上甚少提到的)及UEditor自己的一些內部瑕疵,做為經驗分享給大家,希望大家在以後的整合過程中避免可能會重複陷入的坑提供一些幫助和提醒。
圖 UEditor工具條
圖 UEditor功能展示
UEditor是一款功能相對比較強大的前端富文字編輯器,支援四種後臺語言環境,比如 php,asp,asp.net,jsp 。 UEditor 是由百度「FEX前端研發團隊」開發的所見即所得富文字web編輯器,具有輕量,可定製,注重使用者體驗等特點,開源基於MIT協議,允許自由使用和修改程式碼。
百度官方UEditor文件及下載地址為
百度UEditor官網上,可以檢視並下載UEditor原始碼,初始瞭解UEditor功能及體驗。由於ueditor開發組是一個前端團隊,而對後端實力較欠缺(官方原話),筆者在整合過程中發現其對後臺的處理還有些瑕疵(比如圖片上傳及回顯處理或檔案上傳及下載處理),特別是沒有具體前後臺互動的實際案例(能讓開發者在本地實際執行的DEMO)開發者實際除錯成功,便能快速地掌握前後臺互動的機制。下圖為百度FEX團隊的對後端部署的說明
二、官方DEMO
官方下載地址
本文以 JSP版本為例 下載UTF-8版本並解壓
用瀏覽器開啟index.html
看到demo介面,興奮了一陣子,然而涉及到後臺互動的功能(如圖片檔案上傳),暫時是不能用的,按F12檢視瀏覽器控制檯發現有錯誤
由於官方demo沒有後臺程式碼的實際支撐,所以實際整合過程中會遇到各種問題,或許一時沒有頭緒,如果有一個實際能執行的案例就好了。
筆者參考csdn網站博主liuyazhang的一篇文章
拜讀之後才開始自己的整合之路,編寫並提供了可實際執行的DEMO也是在博主的例子的基礎之上,修復了若干問題後才整合成功的。解決了主要問題是,圖片上傳、儲存、前端頁面顯示;檔案上傳、儲存、前端頁面下載.
三、 動手整合UEditor
3.1 ueditor整體植入結構
本例子整合工具使用的eclipse,用流行的idea當然也是可以的,ueditor外掛目錄如下所示
工程中需要新增以下jar包(Ueditor中就有),如果工程使用Mevan則新增到Mevan中
3.2 富文字頁面入口index.jsp
引入富文字編輯入口頁面index.jsp如下所示
引用了ueditor.config.js,ueditor.all.js,zh-cn.js,index.js和jquery-1.12.0.min.js
控制元件容器載體可以用<textarea> , <div>等
控制元件初始化方法 UE.getEditor(‘myEditor’)
也可以使用<div>加<script>來定義控制元件容器載體,初始化方法大同小異
無論是<textarea><div><script>等標籤做為控制元件容器,最終都會被UEditor所渲染成控制元件。
3.2 關於config.json
很多資料都提到了config.json檔案是配置引數的檔案,然而筆者發現引數定義在本檔案中並不起作用,實際上配置引數定義在ueditor.config.js檔案中才會真正起到作用
3.3 關於ueditor.config.js
可以說是ueditor最重要的配置檔案,後臺配置基本定義在此。
官方檔案如上所示,定義了serverUrl 伺服器統一請求介面路徑jsp/controller.jsp
在ueditor目錄中能找到這個controller.jsp,這個jsp僅僅是用來模擬後臺的入口,真實整合過程中應該是呼叫後臺Controller類而不是這個jsp
整合完成之後的ueditor.config.js參考如下contextRootPath :
由於整合過程中很多地方會用到工程根目錄,這裡就定義好即可,與工程名保持一致即可。
serverUrl:
伺服器統一請求介面路徑 比如 ../ueditor/config(參考UEditorController)
imageActionName:
定義了圖片上傳後臺類及方法 比如 /resource/upload/images (參考UploadImageController)
imageUrl:
圖片頁面回顯後臺類及方法 比如 : /resource/upload/viewImagesToPage.do?imagePath=(參考UploadImageController)
fileActionName:
檔案上傳設定 比如 /resource/upload/images(參考UploadImageController)
fileUrlPrefix:
檔案下載後臺 比如 /ueditor/resource/upload/fileDownLoad.do?(參考UploadImageController)
3.4 serverUrl與伺服器統一請求控制類UEditorController
本類替換原來官方提供的後臺入口controller.jsp ,參考程式碼如下
import com.baidu.ueditor.ActionEnter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope;
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;
/**
* @classname: UEditorController
* @description: 定義 UEditor控制層
* @author: lixy
*/
@Controller
@Scope("prototype")
@RequestMapping("/uedit")
public class UEditorController {
private static final Logger logger = LoggerFactory.getLogger(UEditorController.class);
@RequestMapping(value="/config")
public void config(HttpServletRequest request, HttpServletResponse response) {
response.setContentType("application/json");
String rootPath = request.getSession()
.getServletContext().getRealPath("/");
System.out.println("進入UEditorController");
try {
String exec = new ActionEnter(request, rootPath).exec();
PrintWriter writer = response.getWriter();
writer.write(exec);
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
本類是實現圖片上傳至服務指定目錄並在頁面回顯的指定服務類,同時也是檔案上傳及下載的服務類,與配置檔案ueditor.config.js中定義的以下引數有關聯
imageActionName:
定義了圖片上傳後臺類及方法 比如 /resource/upload/images
imageUrl:
圖片頁面回顯後臺類及方法 比如 : /resource/upload/viewImagesToPage.do?imagePath=
fileActionName:
檔案上傳設定 比如 /resource/upload/images
fileUrlPrefix:
檔案下載後臺 比如 /ueditor/resource/upload/fileDownLoad.do?
程式碼參考如下
圖片檔案上傳及儲存方法imagesimport java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import com.lyz.utils.LoadPropertiesDataUtils;
import com.lyz.utils.RandomUtils;
import com.lyz.utils.StringUtils;
/**
* 上傳圖片的controller
* @author lixy
*
*/
@Controller
@RequestMapping(value="/resource/upload")
public class UploadImageController {
/**
* 上傳圖片
* @param file
* @param request
* @param response
* @return MultipartFile uploadFile,
*/
@ResponseBody
@RequestMapping(value="/images",method = RequestMethod.POST)
public Map<String, Object> images (@RequestParam(value = "file", required = false) MultipartFile file, HttpServletRequest request, HttpServletResponse response){
Map<String, Object> params = new HashMap<String, Object>();
if (file == null){
System.out.println("未獲得上傳檔案!");
return null;
}
try{
String filetype = request.getParameter("filetype")+"";
System.out.println("filetype:"+filetype);
String basePath = "";
if("image".equals(filetype)){
basePath = LoadPropertiesDataUtils.getValue("lixy.uploading.url.image");
}else if("file".equals(filetype)){
basePath = LoadPropertiesDataUtils.getValue("lixy.uploading.url.file");
}
if(basePath == null || "".equals(basePath)){
basePath = "d:/lixy/static"; //與properties檔案中lyz.uploading.url相同,未讀取到檔案資料時為basePath賦預設值
}
System.out.println("filename:"+file.getOriginalFilename());
String ext = StringUtils.getExt(file.getOriginalFilename());
String fileName = String.valueOf(System.currentTimeMillis()).concat("_").concat(RandomUtils.getRandom(6)).concat(".").concat(ext);
StringBuilder sb = new StringBuilder();
//拼接儲存路徑
sb.append(basePath).append("/").append(fileName);
String visitUrl = basePath.concat("/"+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());
System.out.println("url:"+visitUrl+" original:"+fileName+" filename:"+file.getOriginalFilename()+" type:"+file.getContentType());
} catch (Exception e){
params.put("state", "ERROR");
e.printStackTrace();
}
return params;
}
圖片在JSP頁面回顯方法viewImagesToPage方法
/**
* 供讀取伺服器上傳成功的圖片顯示到jsp上使用(多個圖片迴圈呼叫)
* @param request
* @param response
* @param imagePath 圖片地址
* @return
*/
@ResponseBody
@RequestMapping(value = "/viewImagesToPage")
public String viewImagesToPage(HttpServletRequest request,
HttpServletResponse response,
@RequestParam(value = "imagePath", required = false) String imagePath
) {
System.out.println("imagePath:"+imagePath);
ServletOutputStream out = null;
FileInputStream ips = null;
try {
ips = new FileInputStream(new File(imagePath));
response.setContentType("multipart/form-data");
out = response.getOutputStream();
// 讀取檔案流
int i = 0;
byte[] buffer = new byte[4096];
while ((i = ips.read(buffer)) != -1) {
// 寫檔案流
out.write(buffer, 0, i);
}
out.flush();
ips.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (out != null) {
try {
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (ips != null) {
try {
ips.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
return null;
}
檔案在頁面下載方法fileDownLoad
/**
* 下載檔案
* @param request
* @param response
* @return
* @throws IOException
*/
@RequestMapping(value = "/fileDownLoad",method = RequestMethod.GET)
public void fileDownLoad(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 下載本地檔案
String url = request.getParameter("url")+"";
String fileName = request.getParameter("filename")+"";
//如果是IE瀏覽器,則用URLEncode解析
if(isMSBrowser(request)){
fileName = URLEncoder.encode(fileName, "UTF-8");
}else{//如果是谷歌、火狐則解析為ISO-8859-1
fileName = new String(fileName.getBytes("UTF-8"), "ISO-8859-1");
}
System.out.println("filename:"+fileName+" url:"+url);
// 讀到流中
InputStream inStream = new FileInputStream(url);// 檔案的存放路徑
// fileName = url.substring(url.lastIndexOf("/")+1);
// System.out.println("filename:"+fileName);
// 設定輸出的格式
response.reset();
// response.setContentType("bin");
response.setContentType("application/vnd.ms-excel;charset=utf-8");
response.addHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
// 迴圈取出流中的資料
byte[] b = new byte[100];
int len;
try {
while ((len = inStream.read(b)) > 0)
response.getOutputStream().write(b, 0, len);
inStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
3.6 關於圖片上傳涉及 ueditor.all.js中的一些問題
- 圖片上傳至後臺 MultipartFile為空
原碼24486行涉及到圖片上傳的部分程式碼
name="' +me.options.imageFieldName + '" ' 需要與後臺images方法中的 MultipartFilefile名稱保持一致
如不然,則會取不到值MultipartFile物件為空
- 後端配置項沒有正常載入,上傳外掛不能正常使用!
常出現於24557行me.getOpt('imageActionName') 獲值出錯 原因為ueditor.config.js中未定義imageActionName 圖片後臺服務路徑
注意:上面程式碼對圖片上傳的路徑做了調整並新增檔案型別為image的引數(為儲存路徑的引數)
- 上傳後圖片無法回顯
原碼24524行 對圖片路徑處理的調整 其中json.url 是後臺方法image賦值過去的
瀏覽器控制檯列印效果 (圖片顯示連結)
- 上傳excel檔案後,頁面反顯為txt圖片
上傳的明明是excle檔案 顯示的居然是txt圖示 原因是原碼中少了 對 xlsx檔案的對映關係,這裡屬於ueditor的小瑕疵
解決辦法是在原碼中加入對xlsx型別檔案的對映關係即可
再重試 .xlsx類檔案上傳,發現圖示恢復正常。
3.7 關於附件上傳的問題
- attachment.html
建議不引用webuploader.min.js改為引用原webuploader.js以方便處理附件上傳遇到的各種問題
- 檔案上傳路徑
新增以上三行程式碼取得檔案路徑為
檔案型別引數為 file
- 附件上傳路徑問題
attachment.js第146行
actionUrl = editor.getOpt('fileActionName'),考慮不用getActionUrl方法
- 附件下載連結問題
attachment.js第560行
對圖片附件的儲存路徑定義在applications.properties檔案中
後臺的UploadImageController將對請求引數的不同將圖片和附件存放在不同的目錄中
3.9關於其它彈出控制元件被UEditor遮擋住的問題
在編輯頁面如果使用其它彈出層控制元件比如樹型選擇控制元件時,發現樹型控制元件彈出後還是被UEditor控制元件給遮擋住了。如下圖所示。
這是由於樹型彈出層 zIndex為 101 而UEditor的z-Index為999 故而被UEditor遮擋住
解決辦法為 可以將UEditor的z-Index調低一些,將ueditor.all.js行8059 zIndex調成100即可
調整後,再看
彈出樹形控制元件層在UEditor層之上,問題解決。
四、 案例分享
本文提供的完全與文章內容一致的案例,可供讀者實際在本地執行
4.1 案例下載地址
4.2 執行準備工作
本案例由於是SpirngMVC整合需要連線Mysql資料庫,資料庫的連線配置檔案為applicationContext.xml,配置舉例如下
讀者在本地配置好mysql資料庫即可
圖片上傳按鈕和附件上傳按鈕如下圖左側和右側
實際執行效果如下 本地服務連結地址參考如下
http://127.0.0.1:8080/ueditor/index/page
UEditor還有更多好玩的功能,比如 塗鴉功能 ,視訊音訊上傳及線上播放功能,這些功能將會在下一篇文章中向有興趣的讀者介紹,敬請期待。。。