Java 设置添加ckeditor图片上传功能

本文详细介绍CKEditor的集成步骤及图片上传功能实现方法,包括配置文件详解、JSP页面设置、Struts1中处理图片上传的Action代码,并提供了解决上传过程中常见问题的方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >






第一步:
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.youkuaiyun.com/quzishen/article/details/5834207
 6.ckeditor工具缓存比较严重,开发的时候修改他的js文件后每次都需要清浏览器缓存才能起作用。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值