Java 設定新增ckeditor圖片上傳功能
阿新 • • 發佈:2019-02-02
第一步: 1.ckeditor中配置檔案 config。js /** * @license Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.html or http://ckeditor.com/license */ CKEDITOR.editorConfig = function( config ) { config.toolbar = 'Full'; config.toolbar_Full = [ { name: 'mode' }, { name: 'document', items : ['Source','-','Save','NewPage','DocProps','Preview','Print','-','Templates' ] }, { name: 'clipboard', items : [ 'Cut','Copy','Paste','PasteText','PasteFromWord','-','Undo','Redo' ] }, { name: 'editing', items : [ 'Find','Replace','-','SelectAll','-','SpellChecker', 'Scayt' ] }, { name: 'forms', items : [ 'Form', 'Checkbox', 'Radio','TextField', 'Textarea', 'Select', 'Button', 'ImageButton', 'HiddenField' ] }, '/', { name: 'basicstyles', items : ['Bold','Italic','Underline','Strike','Subscript','Superscript','-','RemoveFormat' ] }, { name: 'paragraph', items : ['JustifyLeft', 'JustifyCenter', 'JustifyRight','JustifyBlock' , 'NumberedList','BulletedList','-','Outdent','Indent','-','Blockquote','CreateDiv', '-','JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock','-','BidiLtr','BidiRtl'] }, { name: 'links', items : [ 'Link','Unlink','Anchor' ] }, { name: 'insert', items: ['Image','Flash','Table','HorizontalRule','Smiley','SpecialChar','PageBreak','Iframe' ]}, '/', { name: 'styles', items : [ 'Styles','Format','Font','FontSize' ] }, { name:'colors', items : [ 'TextColor','BGColor' ] }, { name: 'tools', items : [ 'Maximize','ShowBlocks','-','About' ] } ]; config.toolbar = 'MyToolbar'; //其中'-'表示工具之間分隔的豎線,'/'表示換行 ,其中下面的程式碼分別代表什麼可以去網上查,這裡不加以贅述。 config.toolbar_MyToolbar = [ { name: 'document', items : [ 'Preview','-','Templates'] }, { name: 'clipboard', items : [ 'Cut','Copy','Paste','PasteText','PasteFromWord','-','Undo','Redo' ] }, { name: 'insert', items : [ 'Image','Table','HorizontalRule','Smiley','SpecialChar' ] }, { name: 'links', items : [ 'Link','Unlink','Anchor' ] }, { name: 'tools', items : [ 'Maximize' ] }, '/', { name: 'styles', items : [ 'Styles','Format' ] }, { name: 'basicstyles', items : [ 'Bold','Italic','Underline' ] }, { name: 'paragraph', items : [ 'JustifyLeft', 'JustifyCenter', 'JustifyRight','JustifyBlock' ,'NumberedList','BulletedList','-','Outdent','Indent','-','Blockquote' ] } ]; config.removeDialogTabs = 'image:advanced;link:advanced'; config.pasteFromWordRemoveStyles = true; config.image_previewText=' '; //預覽區域顯示內容(預覽區域在開始的時候會有一大串英文,在此將這些英文去除) //獲得action的相對路徑 var pathName = window.document.location.pathname; //獲取帶"/"的專案名 var projectName = pathName.substring(0, pathName.substr(1).indexOf('/') + 1); //加上改行程式碼後就會顯示上傳功能,但是“上傳到伺服器”按鈕不能正常使用,這裡配置的action就是處理圖片上傳的後臺邏輯 config.filebrowserUploadUrl=projectName+'/jxhd/uploadImAction.do?'; //固定路徑 }; ============================================================================================================================================ 第二步: 2.設定jsp頁面,引用ckeditor //引入ckeditor的js檔案,src中為ckeditor.js的路徑 <script type="text/javascript" src='<%=host%>/thirdparty/ckeditor/ckeditor.js'></script> //使用ckeditor的程式碼部分 ····· <td> <common:textarea name="xxlbActionForm" property="content" validator="text(0,3000)" empty="true" styleId="contentid" cols="1" rows="8" label="內容" /> <script type="text/javascript"> //使用ckeditor,這裡可以設定ckeditor的一些屬性,比如高度和寬度,工具欄等等 CKEDITOR.replace("contentid",{toolbar : 'MyToolbar',width:600,height:200}); </script> </td> ····· //js獲得ckeditor框中內容, <script type="text/javascript"> function fun_save(){ //獲取當前頁面部分內容 var ckEditor = CKEDITOR.instances.contentid; //其中contentval是以html的格式 var contentval = ckEditor.getData(); ···· } </script> ============================================================================================================================================ 第三步: 2。Struts1中後臺處理圖片上傳的action程式碼 /** * uploadImAction.java * @date 2015年3月6日 * @author xiaomage */ import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Iterator; import java.util.List; import javax.imageio.ImageIO; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileItemFactory; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.time.DateUtils; import org.apache.log4j.Logger; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import sun.misc.BASE64Encoder; import com.cvicse.exception.DataAccessException; import com.cvicse.infra.base.BaseAction; /** * @author ma_hju * */ public class UploadImAction extends BaseAction { /** * * @param mapping * ActionMapping 對映物件 * @param form * ActionForm 表單物件 * @param request * HttpServletRequest 請求 * @param response * HttpServletResponse 響應 * @throws DataAccessException * @throws ObjectNotExistException */ /** * <p>功能描述:[欄位功能描述]</p> */ private static final long serialVersionUID = 1L; protected final Logger logger = Logger .getLogger(UploadImAction.class); /** ~~~ 上傳檔案的儲存路徑 */ private static final String FILE_UPLOAD_DIR = "/upload"; /** ~~~ 上傳檔案的儲存的下一級路徑,標示儲存型別 */ private static final String FILE_UPLOAD_SUB_IMG_DIR = "/img"; /** ~~~ 為了能讓CKEDITOR載入到上傳的圖片,此處將位置限制在了/file/jxhdxxlb下,這個資料夾可以自定義*/ private static final String FOR_FREEMARKER_LOAD_DIR = "/file/jxhdxxlb"; /** ~~~ 每個上傳子目錄儲存的檔案的最大數目 */ private static final int MAX_NUM_PER_UPLOAD_SUB_DIR = 500; /** ~~~ 上傳檔案的最大檔案大小 */ private static final long MAX_FILE_SIZE = 1024 * 1024 * 2; /** ~~~ 系統預設建立和使用的以時間字串作為檔名稱的時間格式*/ private static final String DEFAULT_SUB_FOLDER_FORMAT_AUTO = "yyyyMMdd"; /** ~~~ 這裡擴充一下格式,防止手動建立的不統一*/ private static final String DEFAULT_SUB_FOLDER_FORMAT_NO_AUTO = "yyyy-MM-dd"; public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws DataAccessException { response.setCharacterEncoding("UTF-8"); PrintWriter out=null; // 判斷提交的請求是否包含檔案 boolean isMultipart = ServletFileUpload.isMultipartContent(request); if (!isMultipart) { //return; } // 獲取目錄 File floder = buildFolder(request); if (null == floder) { //return; } //獲得ckeditor的一個重要引數 String callback =request.getParameter("CKEditorFuncNum"); //獲得專案名 String xmName = request.getContextPath(); //String path = request.getSession().getServletContext().getRealPath("/"); //StringBuffer URL = request.getRequestURL(); try{ response.setContentType("text/html; charset=UTF-8"); response.setHeader("Cache-Control", "no-cache"); out = response.getWriter(); // 上傳檔案的返回地址 String fileUrl = ""; //----start----從request中獲得上傳檔案的方法,此為核心程式碼 FileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload servletFileUpload = new ServletFileUpload(factory); //中文路徑、上傳圖片名中文亂碼問題解決程式碼 servletFileUpload.setHeaderEncoding("UTF-8"); servletFileUpload.setFileSizeMax(MAX_FILE_SIZE); @SuppressWarnings("unchecked") List<FileItem> fileitem = servletFileUpload.parseRequest(request); if (null == fileitem || 0 == fileitem.size()) { //return; } Iterator<FileItem> fileitemIndex = fileitem.iterator(); if (fileitemIndex.hasNext()) { FileItem file = fileitemIndex.next(); if (file.isFormField()) { logger.error("上傳檔案非法!isFormField=true"); } //----end----從request中獲得上傳檔案的方法,此為核心程式碼,file中就是上傳的檔案資訊 //file.getName()獲得檔案在本地的地址,getFileName()方法獲得檔名 String fileClientName = getFileName(file.getName()); //fileFix為該檔案的格式 String fileFix = StringUtils.substring(fileClientName, fileClientName.lastIndexOf(".") + 1); //判斷圖片的格式 if (!StringUtils.equalsIgnoreCase(fileFix, "jpg") && !StringUtils.equalsIgnoreCase(fileFix, "jpeg") && !StringUtils.equalsIgnoreCase(fileFix, "bmp") && !StringUtils.equalsIgnoreCase(fileFix, "gif") && !StringUtils.equalsIgnoreCase(fileFix, "png")) { logger.error("上傳檔案的格式錯誤=" + fileFix); //return; } if (logger.isInfoEnabled()) { logger.info("開始上傳檔案:" + file.getName()); } // 為了客戶端已經設定好了圖片名稱在伺服器繼續能夠明確識別,這裡不改名稱 File newfile = new File(floder, fileClientName); //開始上傳檔案 file.write(newfile); if (logger.isInfoEnabled()) { logger.info("上傳檔案結束,新名稱:" + fileClientName + ".floder:" + newfile.getPath()); } // 組裝返回url,以便於ckeditor定點陣圖片 fileUrl = FOR_FREEMARKER_LOAD_DIR + FILE_UPLOAD_DIR + FILE_UPLOAD_SUB_IMG_DIR + File.separator+floder.getName()+ File.separator + newfile.getName(); fileUrl = fileUrl.substring(1);// 去掉/jxhdxxlb的第一個/,否則ckeditor不識別 fileUrl = StringUtils.replace(fileUrl, "\\", "/"); // 返回“影象”選項卡並顯示圖片,這裡是ckeditor返回影象並顯示的程式碼 out.println("<script type=\"text/javascript\">"); out.println("window.parent.CKEDITOR.tools.callFunction(" + callback+ ",'" + xmName+"/"+fileUrl+ "','')"); // 相對路徑用於顯示圖片 out.println("</script>"); } out.flush(); out.close(); }catch(Exception e){ response.setContentType("text/html;charset=UTF-8"); out.println("<script type=\"text/javascript\">"); out.println("window.parent.CKEDITOR.tools.callFunction("+ callback + ",'','圖片上傳失敗,請重新上傳!','')"); out.println("</script>"); } return null ; } /** * 獲取檔名稱 * @param str * @return */ public String getFileName(String str){ int index = str.lastIndexOf("\\"); if(-1 != index){ return str.substring(index+1); } else { return str; } } /** * 建立目錄 * * @return */ public File buildFolder(HttpServletRequest request) { // 這裡照顧一下CKEDITOR,由於ftl放置位置的原因,這裡必須要在freemarker目錄下才能被載入到圖片,否則雖然可以正常上傳和使用,但是 // 在控制元件中無法正常操作 String realPath = request.getSession().getServletContext() .getRealPath(FOR_FREEMARKER_LOAD_DIR); logger.error(realPath); // 一級目錄,如果不存在,建立 File firstFolder = new File(realPath + FILE_UPLOAD_DIR); if (!firstFolder.exists()) { if (!firstFolder.mkdir()) { //return null; } } // 二級目錄,如果不存在,建立 String folderdir = realPath + FILE_UPLOAD_DIR + FILE_UPLOAD_SUB_IMG_DIR; if (logger.isDebugEnabled()) { logger.debug("folderdir" + folderdir); } if (StringUtils.isBlank(folderdir)) { logger.error("路徑錯誤:" + folderdir); //return null; } File floder = new File(folderdir); if (!floder.exists()) { if (!floder.mkdir()) { logger.error("建立資料夾出錯!path=" + folderdir); //return null; } } // 再往下的資料夾都是以時間字串來命名的,所以獲取最新時間的資料夾即可 String[] files = floder.list(); if (null != files && 0 < files.length) { // 含有子資料夾,則獲取最新的一個 Date oldDate = null; int index = -1; for (int i = 0; i < files.length; i++) { String fileName = files[i]; try { Date thisDate = DateUtils.parseDate(fileName, new String[] { DEFAULT_SUB_FOLDER_FORMAT_AUTO, DEFAULT_SUB_FOLDER_FORMAT_NO_AUTO }); if (oldDate == null) { oldDate = thisDate; index = i; } else { if (thisDate.after(oldDate)) { // 儲存最新的時間和陣列中的下標 oldDate = thisDate; index = i; } } } catch (ParseException e) { // 這裡異常吃掉,不用做什麼,如果解析失敗,會建立新的資料夾,防止人為的建立資料夾導致的異常。 } }// for // 判斷當前最新的資料夾下是否已經存在了最大數目的圖片 if (null != oldDate && -1 != index) { File pointfloder = new File(folderdir + File.separator + files[index]); if (!pointfloder.exists()) { if (!pointfloder.mkdir()) { logger.error("建立資料夾出錯!path=" + folderdir); //return null; } } // 如果資料夾下的檔案超過了最大值,那麼也需要新建一個資料夾 String[] pointfloderFiles = pointfloder.list(); if (null != pointfloderFiles && MAX_NUM_PER_UPLOAD_SUB_DIR < pointfloderFiles.length) { return buildNewFile(folderdir); } return pointfloder; } // 查詢當前子資料夾失敗,新建一個 return buildNewFile(folderdir); } else { // 不含有子資料夾,新建一個,通常系統首次上傳會有這個情況 return buildNewFile(folderdir); } } /** * 建立一個新檔案 * @param path * @return */ public File buildNewFile(String path){ // 不含有子資料夾,新建一個,通常系統首次上傳會有這個情況 File newFile = buildFileBySysTime(path); if (null == newFile) { logger.error("建立資料夾失敗!newFile=" + newFile); } return newFile; } /** * 根據當前的時間建立資料夾,時間格式yyyyMMdd * * @param path * @return */ public File buildFileBySysTime(String path) { DateFormat df = new SimpleDateFormat(DEFAULT_SUB_FOLDER_FORMAT_AUTO); String fileName = df.format(new Date()); File file = new File(path + File.separator + fileName); if (!file.mkdir()) { return null; } return file; } } ================================================================================================================================ 第四步: Struts檔案中要配置action的跳轉 <!-- 圖片上傳 start--> <action path="/uploadImAction" scope="request" type="com.cvicse.szxy.jxhd.xxlb.action.UploadImAction" > </action> 注意:這裡配置時不要配置form,原因是struts1.2中ActionForm和ServletFileUpload.parseRequest(request)不能同時使用。 網上匿名高人是這麼解釋的: 解釋struts用ActionForm的方式處理上傳附件的一些問題,struts接收到enctype="multipart/form-data"的post請求後,會看那 個對應的action有沒有配置actionform,如果配置了,就會作一些處理,所以你在action裡得到的request已經不是一個普通的request 了,而是一個被封裝過的request。如果想得到原始的request,就不要struts-config.xml裡給action類配置actionform。 ServletFileUpload.parseRequest(request)中的request用的是普通的request,而使用actionForm時request被封裝,從而 導致ServletFileUpload.parseRequest(request)取不到值,為空。目前來說,無法解決ActionForm和ServletFileUpload.parseRequest(request) 共存問題,那隻能換別的上傳方式了! 詳情可參考:http://blog.sina.com.cn/s/blog_4873f8e00100qxnn.html ============================================================================================================================================================== 至此ckeditor功能基本上是配置完成了。 下面說一下在配置過程中可能遇到的一些問題和解決方法: 1.上傳圖片時路徑或圖片名含中文有亂碼問題。 解決方法:新增一下程式碼 //中文路徑、上傳圖片名中文亂碼問題解決程式碼 servletFileUpload.setHeaderEncoding("UTF-8"); 2。圖片能上傳到伺服器但是預覽時不能檢視圖片。 主要原因是:返回影象地址時路徑不正確 // 返回“影象”選項卡並顯示圖片,這裡是ckeditor返回影象並顯示的程式碼 out.println("<script type=\"text/javascript\">"); out.println("window.parent.CKEDITOR.tools.callFunction(" + callback+ ",'" + xmName+"/"+fileUrl+ "','')"); // 相對路徑用於顯示圖片 out.println("</script>"); 解決方法:根據自己的實際情況修改返回的圖片路徑。 3.點選“上傳到伺服器”按鈕,顯示找不到網頁錯誤,即跳轉不到action 可能原因:檢視Struts中的action跳轉的配置,看是否配置了actionform,struts1.2中ActionForm和ServletFileUpload.parseRequest(request)不能同時使用。 4.該action中儲存圖片用的是三級目錄,我也是引用網上大神的程式碼,對此大神是這樣說的: “寫程式碼前,看看我們的現狀吧,我們可能會讓這個圖片上傳到圖片伺服器去,但是呢,兜裡尚未有足夠的銀子,而且這個圖片暫時量不大, 所以現階段還是儲存在應用的特定位置中,夠無奈的吧,沒辦法,誰讓咱麼有特定的圖片伺服器呢,那麼就下辦法在本應用下作文章吧。 我們採用一個upload/img的目錄,來儲存圖片檔案,以後要遷移到圖片伺服器也方便些。但是問題來了,所有的圖片都放到這個資料夾下, 豈不是很龐大,而且一旦超過1000張,檔案搜尋速度是有點折磨的,那就咱想想辦法吧,那就再建立一級目錄,每一級下面最多放500張, 如果當前資料夾下超過了500張,就重新建立一個資料夾,放入其中。這樣目錄就變成了三級的 upload/img/20100824 我們採用時間字串來命名。” 5.這個action方法也是我參考網上大神的程式碼,根據自己專案的需求加以修改。我專案使用的是Struts1。 詳情可參考:http://blog.csdn.net/quzishen/article/details/5834207 6.ckeditor工具快取比較嚴重,開發的時候修改他的js檔案後每次都需要清瀏覽器快取才能起作用。