kindEditor官網:http://kindeditor.net/demo.phpjavascript
我的實踐:php
爲了在本身的項目中引入一個相似用戶寫博客的功能,在網上找到了kindeditor,真心又好又易用。css
1、準備工做html
一、下載kindeditor,在官網上點擊右上方的下載連接,我下載的是4.1.11版。因爲個人項目前端採用jsp,因此將解壓之後的asp,asp.net和php文件夾刪掉。前端
在jsp文件夾裏的就是在jsp下使用kindeditor的demo。須要注意的是,demo.jsp文件中有筆誤,須要首先將<script charset="utf-8" src="../kindeditor.js"></script>java
的src指向kindeditor-all-min.js才能夠。jquery
二、閱讀demo.jsp:kindeditor的js使用K.create方法,於頁面加載完畢之後,在name是content1的textarea前面自動建立博客編輯功能toolbar,最關鍵的uploadJson參數指定ajax
了用戶上傳圖片、動畫、文件等媒體文件的後臺處理controller。在demo.jsp中,表單提交給了demo.jsp本身,能夠在jsp代碼的開頭部分發現,程序從request中將content1的spring
內容取了出來,在body中,將內容展現在form表單以前。也就是說demo.jsp中的博客編輯器提交後的內容會在編輯器上方展現出來。須要注意的是htmlspecialchars方法,它數據庫
將html標籤中的相關字符進行轉義,目的是將用戶提交的內容在博客編輯器中顯示,方便用戶繼續編輯已經存在的內容。這個轉義的過程在項目中咱們實際上是放在後臺處理的,
作好之後直接傳給前臺展現。fileManagerJson參數指定了上傳後文件的編輯界面,因爲個人項目不須要這個功能,因此將這個參數捨去,將allowFileManager參數置false。
afterCreate參數指定了建立博客編輯功能toolbar之後作的事,就是當用戶使用ctrl+enter的時候提交表單。我不須要這功能因此也能夠捨去afterCreate。
三、閱讀完了demo.jsp之後,如何展現編輯工具欄,如何提交表單,如何在編輯框中顯示已經存在的內容,這幾個關鍵的基本問題就都找到答案了,下面就是實踐和解決細節問題。
主要是兩個後臺問題,一是如何處理uploadJson參數對應的文件上傳及給kindeditor返回準確的值,二是表單提交之後如何在後臺取到提交的內容,因爲我使用了springmvc的form
標籤庫,如何結合標籤庫也是個小問題。
2、編碼和實現
一、jsp文件片斷
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>${pageTitle }</title> <link href="/Public/media/css/new/page.css" rel="stylesheet" type="text/css" /> <link rel="stylesheet" href="/Public/kindeditor/themes/default/default.css" /> <link rel="stylesheet" href="/Public/kindeditor/plugins/code/prettify.css" /> <script charset="utf-8" src="/Public/kindeditor/kindeditor-all-min.js"></script> <script charset="utf-8" src="/Public/kindeditor/lang/zh-CN.js"></script> <script charset="utf-8" src="/Public/kindeditor/plugins/code/prettify.js"></script> <script> KindEditor.ready(function(K) { var editor1 = K.create('textarea[name="content"]', { cssPath : '/Public/kindeditor/plugins/code/prettify.css', uploadJson : '${pageContext.request.contextPath}/workConfig/updateContent', allowFileManager : false }); prettyPrint(); }); </script> </head> <body> <form:form action="${formActionURL}" method="post" commandName="work"> <div class="formDiv"> <table width="1020" border="0"> <tr> <td align="right">展現頁面:</td> <td align="left"> <textarea name="content" rows="20" cols="100" style="width: 700px; height: 200px; visibility: hidden;"> ${work.content } </textarea> </td> </tr> </table> </div> </form:form> </body> </html>
1.1:曾嘗試過使用springmvc的textarea標籤庫<form:textarea,雖然只要將kindeditor的K.create裏textarea的name改成<form:textarea的name便可(假設form:textarea的path值爲"content",那麼這個標籤庫在被翻譯成html時,textarea的name和id會被自動賦值爲"content"),但因爲textarea的內容會被springmvc自動賦值給表單表明的Model類的相應值,因此htmlspecialchars的轉義過程就無法進行了,固然你也能夠修改JavaBean的
getContent方法中加入轉義過程,但這麼作稍顯繁瑣,不如就不用form:textarea標籤庫,只要將其name值設置爲表單Model類的屬性值,內容在提交後同樣會被自動賦值給對應的屬性(經試驗證明)。
二、文件上傳處理程序片斷
@RequestMapping(value="/updateContent",produces = "application/json; charset=utf-8") @ResponseBody public String updateContent(MultipartHttpServletRequest request) { MultipartFile realMediaFile = null; Iterator<String> iter = request.getFileNames(); JSONObject obj = new JSONObject(); while (iter.hasNext()) { // 取得上傳文件 MultipartFile file = request.getFile(iter.next()); if (file != null) { realMediaFile = file; } if (realMediaFile != null) { // 定義容許上傳的文件擴展名 String extStr = "gif,jpg,jpeg,png,bmp,swf,flv,mp3,wav,wma,wmv,mid,avi,mpg,asf,rm,rmvb,doc,docx,xls,xlsx,ppt,htm,html,txt,zip,rar,gz,bz2"; List<String> extList = Arrays.asList(extStr.split(",")); // 最大文件大小 long maxSize = 1000000; // 準備好路徑參數 String uploadMediaSavePath = pathUtil.getYgbhUploadMediaDiskFolderPath(request); String urlRootPath = pathUtil.getYgbhUploadMediaUrlFolderPath(); List<MultipartFile> files = new ArrayList<MultipartFile>(); files.add(realMediaFile); // 保存 String message = ""; if (realMediaFile.getSize() > maxSize) { message = "上傳文件大小超過限制。"; } String originalfileName = realMediaFile.getOriginalFilename(); String suffixName = originalfileName.substring(originalfileName.lastIndexOf(".") + 1).toLowerCase(); if (!extList.contains(suffixName)) { message = "上傳文件擴展名是不容許的擴展名。\n只容許[" + extStr + "]格式。"; } String newFileName = ""; try { newFileName = MultipartFileUtil.saveMediaFiles(files, uploadMediaSavePath, urlRootPath).get(0); } catch (Exception e) { message = "上傳文件失敗。"; } try { if (!message.isEmpty()) { obj.put("error", 1); obj.put("message", message); } else { obj.put("error", 0); obj.put("url", urlRootPath + newFileName); } } catch (Exception e) { log.error("上傳文件[" + originalfileName + "]失敗。"); e.printStackTrace(); } } else { try { obj.put("error", 1); obj.put("message", "找不到文件。"); } catch (JSONException e) { log.error("上傳文件失敗。"); e.printStackTrace(); } } return obj.toString(); } return obj.toString(); }
2.1:使用@ResponseBody的返回值爲String類型的方法,在試驗後被證實是可使用的。若是不標識@ResponseBody,返回String類型的Controller方法會將返回值視爲jsp文件的路徑去尋找jsp文件。而若是你打算
將返回值類型用某JavaBean來代替,心想反正使用了@ResponseBody,springmvc會將對象轉換成正確的JSON格式,結果是kindeditor拿不到返回值。
2.2:因爲kindeditor生成的博客編輯工具欄有批量上傳按鈕,因此在取得用戶提交內容時,須要用iterator的方式。
2.3:看到urlRootPath變量了嗎,它指向個人系統的靜態資源工程,是一個只能在本系統內能夠訪問的不跨域url。這也牽扯到後面的一個問題。需求是:用戶本身編寫的博客內容,會以html的方式保存在系統的數據庫中,
該段html代碼必須在它被從數據庫裏取出並放置在一個html頁面的body片斷中時可以完整地展現全部內容,包括圖片、動畫等媒體資源內容。那麼問題來了,不管我怎麼設置,就算是我直接在urlRootPath前拼接「http://
XXX.XXX.XX.XX:8080」,等到內容顯示在textarea編輯框中時,圖片等媒體資源的src依然被kindeditor自動改成不跨域的url,也就是說,只能在系統內展現時才能看見這些非文本的內容。如何優雅地解決該問題我至今
還沒找到很好的辦法,只是暫時在保存博客內容html時,將全部包含不跨域url的內容前面自動加上相似「http://XXX.XXX.XX.XX:8080」的前綴,以保證該資源在任何地方都能被訪問。
補充,上面劃線部分的問題已經獲得解決(參考資料:http://www.111cn.net/wy/255/64719.htm),給KindEditor加一個與uploadJson同級別的運行參數,urlType:'domain',
urlTypeY有四種值,""表示不修改URL,"relative"表示相對路徑,"absolute"表示絕對路徑,"domain表示帶域名的絕對路徑"。
因爲該問題已解決,因此3中的replace過程能夠去掉了。
2.4:produces = "application/json; charset=utf-8"這句若是不加,返回的中文內容會是亂碼。
三、表單提交內容處理程序片斷
@RequestMapping(value = "/editWork", method = RequestMethod.POST) public ModelAndView editWork(@Valid @ModelAttribute("work") DesignerWorks work, BindingResult br, HttpServletRequest request) { ModelAndView mv = new ModelAndView(); mv.addObject("work", work); if (br.hasErrors()) { mv.addObject("currentMenuDesc", "當前目錄:首頁>系統管理>做品管理>編輯做品"); mv.addObject("backToURL", request.getContextPath() + "/workConfig/openWorkConfig"); mv.addObject("formActionURL", request.getContextPath() + "/workConfig/editWork"); mv.addObject("pageTitle", "編輯做品"); mv.setViewName("editWork"); } else { try { DesignerWorks workToSave = work; workToSave.setContent(workToSave.getContent().replace(pathUtil.getServerUrl(request), "")); workToSave.setContent(workToSave.getContent().replace(pathUtil.getUploadMediaRootUrl(), pathUtil.getServerUrl(request) + pathUtil.getUploadMediaRootUrl())); designerService.updateWork(work); } catch (Exception e) { mv.addObject(ConstantsUtil.ERROR_STACK, ExceptionUtils.getStackTrace(e)); mv.setViewName("error"); return mv; } mv.addObject(ConstantsUtil.BACK_TO_URL, request.getContextPath() + "/workConfig/openWorkConfig"); mv.setViewName("success"); } return mv; }
3.1:能夠看到媒體文件url的replace的過程,getServerUrl的代碼片斷以下:
import java.io.File;
import java.net.InetAddress;
import javax.servlet.http.HttpServletRequest;
public static String getServerUrl(HttpServletRequest request) throws Exception{ return "http://"+InetAddress.getLocalHost().getHostAddress()+":"+request.getServerPort(); }
3、補充
一、問題:在以後的項目中,我使用了jquery.easyui.min.js的.form()來間接提交表單,發現由kindeditor生成的textarea內容始終沒法被後臺接收到。
二、調查:查看頁面源碼我發現,kindeditor工做時,是在被它修飾的textarea的上方加了一個div,用戶輸入的全部內容其實都是在編輯div中的內容而不是直接寫在textarea裏,
當用戶使用傳統方式提交表單時,kindeditor必定是自動有一個將用戶輸入內容粘貼入textarea的動做,而當使用js間接提交表單時,kindeditor的這個自動的動做沒有被觸發,因此
後臺獲得的是還未被粘貼內容的textarea的文本。
三、解決:最直接的想辦法是手動調一下這個自動粘貼的程序。搜索了一下發現已有人解決。http://lxy.me/ajax-kindeditor-content-to-textarea.html
<script type="text/javascript"> KindEditor.ready(function(K){ K.create('textarea[name="content"]', { themeType: 'simple', resizeType: 1, uploadJson: 'common/KEditor/upload_json.php', fileManagerJson: 'common/KEditor/file_manager_json.php', allowFileManager: true, //下面這行代碼就是關鍵的所在,當失去焦點時執行this.sync(),同步輸入的值到textarea中; afterBlur: function(){this.sync();} }); });</script>