本文介绍2种使用ckeditor上传文件保存至项目外(即硬盘)的方法。
环境:tomcat + spring
第一种:通过ckeditor+ckfinder搭配。
1)在页面中的配置
引入标签<%@ include file="/WEB-INF/jsp/layouts/taglib_ck.jsp"%>
页面html代码
<textarea name="content" id="content" rows="30"></textarea> <hs:ckeditor replace="content" uploadPath="/cms/content" />
taglib_ck.jsp的内容如下:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%> <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%> <c:set var="ctx" value="${pageContext.request.contextPath}"/> <c:set var="ctxStatic" value="${pageContext.request.contextPath}/scripts"/>
ckeditor.tag的标签如下:
<%@ tag language="java" pageEncoding="UTF-8"%> <%@ include file="/WEB-INF/jsp/layouts/taglib_ck.jsp"%> <%@ attribute name="replace" type="java.lang.String" required="true" description="需要替换的textarea编号"%> <%@ attribute name="uploadPath" type="java.lang.String" required="false" description="文件上传路径,路径后自动添加年份。若不指定,则编辑器不可上传文件"%> <script type="text/javascript">include('ckeditor_lib','${ctxStatic}/ckeditor/',['ckeditor.js']);</script> <script type="text/javascript"> var ${replace}Ckeditor = CKEDITOR.replace("${replace}");//<c:if test="${not empty uploadPath}"> ${replace}Ckeditor.config.ckfinderPath="${ctxStatic}/ckfinder"; var date = new Date(), year = date.getFullYear(), month = (date.getMonth()+1)>9?date.getMonth()+1:"0"+(date.getMonth()+1); ${replace}Ckeditor.config.ckfinderUploadPath="${uploadPath}/"+year+"/"+month+"/";//</c:if> </script>
ckfinder.tag的代码如下:
<%@ tag language="java" pageEncoding="UTF-8"%> <%@ include file="/WEB-INF/jsp/layouts/taglib_ck.jsp"%> <%@ attribute name="input" type="java.lang.String" required="true" description="输入框"%> <%@ attribute name="type" type="java.lang.String" required="true" description="files、images、flash、thumb"%> <%@ attribute name="uploadPath" type="java.lang.String" required="true" description="打开文件管理的上传路径"%> <%@ attribute name="selectMultiple" type="java.lang.Boolean" required="false" description="是否允许多选"%> <ol id="${input}Preview"></ol><a href="javascript:" onclick="${input}FinderOpen();" class="btn">${selectMultiple?'添加':'选择'}</a> <a href="javascript:" onclick="${input}DelAll();" class="btn">清除</a> <script type="text/javascript"> function ${input}FinderOpen(){//<c:if test="${type eq 'thumb'}"><c:set var="ctype" value="images"/></c:if><c:if test="${type ne 'thumb'}"><c:set var="ctype" value="${type}"/></c:if> var date = new Date(), year = date.getFullYear(), month = (date.getMonth()+1)>9?date.getMonth()+1:"0"+(date.getMonth()+1); var url = "${ctxStatic}/ckfinder/ckfinder.html?type=${ctype}&start=${ctype}:${uploadPath}/"+year+"/"+month+ "/&action=js&func=${input}SelectAction&thumbFunc=${input}ThumbSelectAction&cb=${input}Callback&dts=${type eq 'thumb'?'1':'0'}&sm=${selectMultiple?1:0}"; windowOpen(url,"文件管理",1000,700); //top.$.jBox("iframe:"+url+"&pwMf=1", {title: "文件管理", width: 1000, height: 500, buttons:{'关闭': true}}); } function ${input}SelectAction(fileUrl, data, allFiles){ var url="", files=ckfinderAPI.getSelectedFiles(); for(var i=0; i<files.length; i++){//<c:if test="${type eq 'thumb'}"> url += files[i].getThumbnailUrl();//</c:if><c:if test="${type ne 'thumb'}"> url += files[i].getUrl();//</c:if> if (i<files.length-1) url+="|"; }//<c:if test="${selectMultiple}"> $("#${input}").val($("#${input}").val()+($("#${input}").val(url)==""?url:"|"+url));//</c:if><c:if test="${!selectMultiple}"> $("#${input}").val(url);//</c:if> ${input}Preview(); //top.$.jBox.close(); } function ${input}ThumbSelectAction(fileUrl, data, allFiles){ var url="", files=ckfinderAPI.getSelectedFiles(); for(var i=0; i<files.length; i++){ url += files[i].getThumbnailUrl(); if (i<files.length-1) url+="|"; }//<c:if test="${selectMultiple}"> $("#${input}").val($("#${input}").val()+($("#${input}").val(url)==""?url:"|"+url));//</c:if><c:if test="${!selectMultiple}"> $("#${input}").val(url);//</c:if> ${input}Preview(); //top.$.jBox.close(); } function ${input}Callback(api){ ckfinderAPI = api; } function ${input}Del(obj){ var url = $(obj).prev().attr("url"); $("#${input}").val($("#${input}").val().replace("|"+url,"","").replace(url+"|","","").replace(url,"","")); ${input}Preview(); } function ${input}DelAll(){ $("#${input}").val(""); ${input}Preview(); } function ${input}Preview(){ var li, urls = $("#${input}").val().split("|"); $("#${input}Preview").children().remove(); for (var i=0; i<urls.length; i++){ if (urls[i]!=""){//<c:if test="${type eq 'thumb' || type eq 'images'}"> li = "<li><img src=\""+urls[i]+"\" url=\""+urls[i]+"\" style=\"max-width:200px;max-height:200px;_height:200px;border:0;padding:3px;\">";//</c:if><c:if test="${type ne 'thumb' && type ne 'images'}"> li = "<li><a href=\""+urls[i]+"\" url=\""+urls[i]+"\" target=\"_blank\">"+decodeURIComponent(urls[i].substring(urls[i].lastIndexOf("/")+1))+"</a>";//</c:if> li += " <a href=\"javascript:\" onclick=\"${input}Del(this);\">×</a></li>"; $("#${input}Preview").append(li); } } } ${input}Preview(); </script>
最主要的一个引入js的方法:在ckeditor.tag中调用
function include(id, path, file) { if (document.getElementById(id) == null) { var files = typeof file == "string" ? [ file ] : file; for ( var i = 0; i < files.length; i++) { var name = files[i].replace(/^\s|\s$/g, ""); var att = name.split('.'); var ext = att[att.length - 1].toLowerCase(); var isCSS = ext == "css"; var tag = isCSS ? "link" : "script"; var attr = isCSS ? " type='text/css' rel='stylesheet' " : " type='text/javascript' "; var link = (isCSS ? "href" : "src") + "='" + path + name + "'"; document.write("<" + tag + (i == 0 ? " id=" + id : "") + attr + link + "></" + tag + ">"); } } }
2)在ckeditor config中配置上传 ---基于ckfinder自身的上传
CKEDITOR.editorConfig = function( config ) { config.language = 'zh-cn'; config.uiColor = '#f7f5f4'; //config.skin="v2"; config.width = '99.7%'; config.height = '300px'; config.removePlugins = 'elementspath,scayt'; config.disableNativeSpellChecker = false; config.resize_dir = 'vertical'; config.keystrokes =[[ CKEDITOR.CTRL + 13 /*Enter*/, 'maximize' ]]; config.extraPlugins = 'tableresize'; config.enterMode = CKEDITOR.ENTER_P; config.shiftEnterMode = CKEDITOR.ENTER_BR; config.font_names='宋体/宋体;黑体/黑体;仿宋/仿宋_GB2312;楷体/楷体_GB2312;隶书/隶书;幼圆/幼圆;微软雅黑/微软雅黑;'+ config.font_names; config.image_previewText=' '; config.toolbar_default = [ ['Source'], ['Cut','Copy','Paste','PasteText','PasteFromWord','Undo','Redo'], ['Link','Unlink','Anchor'], ['Image','Flash','Table','HorizontalRule','SpecialChar','Smiley'], ['Maximize'], '/', ['Bold','Italic','Underline','Strike','RemoveFormat'], ['NumberedList','BulletedList','-','Outdent','Indent','Blockquote'], ['JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'], ['Styles','Font','FontSize'], ['TextColor','BGColor'] ]; config.toolbar = 'default'; if(config.ckfinderPath){ config.filebrowserBrowseUrl = config.ckfinderPath+'/ckfinder.html?type=files'; config.filebrowserImageBrowseUrl = config.ckfinderPath+'/ckfinder.html?type=images'; config.filebrowserFlashBrowseUrl = config.ckfinderPath+'/ckfinder.html?type=flash'; config.filebrowserUploadUrl = config.ckfinderPath+'/core/connector/java/connector.java?command=QuickUpload&type=files'; config.filebrowserImageUploadUrl = config.ckfinderPath+'/core/connector/java/connector.java?command=QuickUpload&type=images'; config.filebrowserFlashUploadUrl = config.ckfinderPath+'/core/connector/java/connector.java?command=QuickUpload&type=flash'; config.filebrowserWindowWidth = '1000'; config.filebrowserWindowHeight = '700'; } }; CKEDITOR.stylesSet.add( 'default', [ /* Block Styles */ { name : '首行缩进' , element : 'p', styles : { 'text-indent' : '20pt' } }, /* Inline Styles */ { name : '标注黄色' , element : 'span', styles : { 'background-color' : 'Yellow' } }, { name : '标注绿色' , element : 'span', styles : { 'background-color' : 'Lime' } }, /* Object Styles */ { name : '图片左对齐', element : 'img', attributes : { 'style' : 'padding: 5px; margin-right: 5px', 'border' : '2', 'align' : 'left' } }, { name : '图片有对齐', element : 'img', attributes : { 'style' : 'padding: 5px; margin-left: 5px', 'border' : '2', 'align' : 'right' } }, { name : '无边界表格', element : 'table', styles: { 'border-style': 'hidden', 'background-color' : '#E6E6FA' } } ]);
3)将ckfinder.xml放入WEB-INF目录下
4)在web.xml中配置ckfinder
<!-- CKFinder 编辑器可上传图片附件 --> <servlet> <servlet-name>CKFinderConnectorServlet</servlet-name> <servlet-class>hs.ckeditor.CKFinderConnectorServlet</servlet-class> <init-param> <param-name>XMLConfig</param-name> <param-value>/WEB-INF/ckfinder.xml</param-value> </init-param> <init-param> <param-name>debug</param-name> <param-value>false</param-value> </init-param> <init-param> <param-name>configuration</param-name> <param-value>hs.ckeditor.CKFinderConfig</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>CKFinderConnectorServlet</servlet-name> <url-pattern>/scripts/ckfinder/core/connector/java/connector.java</url-pattern> </servlet-mapping> <filter> <filter-name>FileUploadFilter</filter-name> <filter-class>com.ckfinder.connector.FileUploadFilter</filter-class> <init-param> <param-name>sessionCookieName</param-name> <param-value>JSESSIONID</param-value> </init-param> <init-param> <param-name>sessionParameterName</param-name> <param-value>jsessionid</param-value> </init-param> </filter> <filter-mapping> <filter-name>FileUploadFilter</filter-name> <url-pattern>/scripts/ckfinder/core/connector/java/connector.java</url-pattern> </filter-mapping>
其中hs.ckeditor.CKFinderConfig 需要自己定义。
CKFinderConfig.java
import javax.servlet.ServletConfig; import javax.servlet.http.HttpServletRequest; import com.ckfinder.connector.ServletContextFactory; import com.ckfinder.connector.configuration.Configuration; import com.ckfinder.connector.utils.AccessControlUtil; /** * CKFinder配置 * @author selina * @version 2013-08-06 */ public class CKFinderConfig extends Configuration { public CKFinderConfig(ServletConfig servletConfig) { super(servletConfig); } @Override protected Configuration createConfigurationInstance() { AccessControlUtil.getInstance(this).loadACLConfig(); try { this.baseURL = ServletContextFactory.getServletContext().getContextPath()+"/userfiles/" +"/"; } catch (Exception e) { throw new RuntimeException(e); } return new CKFinderConfig(this.servletConf); } @Override public boolean checkAuthentication(final HttpServletRequest request) { //权限验证 return true; } }
CKFinderConnectorServlet.java
import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.ckfinder.connector.ConnectorServlet; /** * CKFinderConnectorServlet * @author selina * @version 2013-08-06 */ public class CKFinderConnectorServlet extends ConnectorServlet { private static final long serialVersionUID = 1L; @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { prepareGetResponse(request, response, false); super.doGet(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { prepareGetResponse(request, response, true); super.doPost(request, response); } private void prepareGetResponse(final HttpServletRequest request, final HttpServletResponse response, final boolean post) throws ServletException { String command = request.getParameter("command"); String type = request.getParameter("type"); // 初始化时,如果startupPath文件夹不存在,则自动创建startupPath文件夹 if ("Init".equals(command)){ String startupPath = request.getParameter("startupPath");// 当前文件夹可指定为模块名 if (startupPath!=null){ String[] ss = startupPath.split(":"); if (ss.length==2){ String path = "/userfiles/"+ss[0]+ss[1]; String realPath = request.getSession().getServletContext().getRealPath(path); FileUtils.createDirectory(realPath); } } } // 快捷上传,自动创建当前文件夹,并上传到该路径 else if ("QuickUpload".equals(command) && type!=null){ String currentFolder = request.getParameter("currentFolder");// 当前文件夹可指定为模块名 String path = "/userfiles/"+type+(currentFolder!=null?currentFolder:""); String realPath = request.getSession().getServletContext().getRealPath(path); FileUtils.createDirectory(realPath); } // System.out.println("------------------------"); // for (Object key : request.getParameterMap().keySet()){ // System.out.println(key + ": " + request.getParameter(key.toString())); // } } }
总的来说就是这些了。
总结:通过结合ckeditor与ckfinder可实现编辑器文件上传并且还提供文件夹供浏览,不过这样设置会把文件等保存在项目内,如果文件过多,就会导致资源过多且不易安装与维护。
其实呢,ckfinder也是比较强大的,通过配置ckfinder.xml中的BaseDir也可以实现把文件保存在项目外的功能。不过要在tomcat配置虚拟路径。说明如下:
#当ckfinder.xml中配置了baseDir路径时,附件保存在项目外。不配置时默认在项目内。 当ckfinder.xml配置如下时: <baseDir>d:/xxAttacm/userfiles/</baseDir> <baseURL>/userfiles/</baseURL> 必须在tomcat server.xml下建立虚拟路径如下: 在<Host></Host>之间加入<Context docBase="d:\xxAttacm\userfiles\" path="/myProject/userfiles" reloadable="true"/> 其中myProject为项目发布根目录。docBase需与baseDir对应。
因该文章过长,为不影响浏览效果,第二种方式《CKEditor文件上传-多种方式-自定义上传-保存在项目外》
将在后续补上。如有不足,欢迎指正!