1. 程式人生 > >Java 設定新增ckeditor圖片上傳功能

Java 設定新增ckeditor圖片上傳功能






第一步:
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檔案後每次都需要清瀏覽器快取才能起作用。