使用springmvc,jsp,结合网页文本编辑器kindEditor实现基本博客编辑功能

kindEditor官网:http://kindeditor.net/demo.php

个人实践:

为了在自己的项目中引入一个类似用户写博客的功能,在网上找到了kindeditor,真心又好又易用。

一、准备工作

1、下载kindeditor,在官网上点击右上方的下载链接,我下载的是4.1.11版。由于我的项目前端采用jsp,所以将解压以后的asp,asp.net和php文件夹删掉。

在jsp文件夹里的就是在jsp下使用kindeditor的demo。需要注意的是,demo.jsp文件中有笔误,需要首先将<script charset="utf-8" src="../kindeditor.js"></script>

的src指向kindeditor-all-min.js才可以。

2、阅读demo.jsp:kindeditor的js使用K.create方法,于页面加载完毕以后,在name是content1的textarea前面自动创建博客编辑功能toolbar,最关键的uploadJson参数指定

了用户上传图片、动画、文件等媒体文件的后台处理controller。在demo.jsp中,表单提交给了demo.jsp自己,可以在jsp代码的开头部分发现,程序从request中将content1的

内容取了出来,在body中,将内容展示在form表单之前。也就是说demo.jsp中的博客编辑器提交后的内容会在编辑器上方展示出来。需要注意的是htmlspecialchars方法,它

将html标签中的相关字符进行转义,目的是将用户提交的内容在博客编辑器中显示,方便用户继续编辑已经存在的内容。这个转义的过程在项目中我们其实是放在后台处理的,

做好以后直接传给前台展示。fileManagerJson参数指定了上传后文件的编辑界面,由于我的项目不需要这个功能,所以将这个参数舍去,将allowFileManager参数置false。

afterCreate参数指定了创建博客编辑功能toolbar以后做的事,就是当用户使用ctrl+enter的时候提交表单。我不需要这功能所以也可以舍去afterCreate。

3、阅读完了demo.jsp以后,如何展示编辑工具栏,如何提交表单,如何在编辑框中显示已经存在的内容,这几个关键的基本问题就都找到答案了,下面就是实践和解决细节问题。

主要是两个后台问题,一是如何处理uploadJson参数对应的文件上传及给kindeditor返回准确的值,二是表单提交以后如何在后台取到提交的内容,由于我使用了springmvc的form

标签库,如何结合标签库也是个小问题。

二、编码和实现

1、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类的属性值,内容在提交后一样会被自动赋值给对应的属性(经试验证实)。

2、文件上传处理程序片段

   @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"这句如果不加,返回的中文内容会是乱码。

3、表单提交内容处理程序片段

@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();
}

三、补充

1、问题:在之后的项目中,我使用了jquery.easyui.min.js的.form()来间接提交表单,发现由kindeditor生成的textarea内容始终无法被后台接收到。

2、调查:查看页面源码我发现,kindeditor工作时,是在被它修饰的textarea的上方加了一个div,用户输入的所有内容其实都是在编辑div中的内容而不是直接写在textarea里,

当用户使用传统方式提交表单时,kindeditor一定是自动有一个将用户输入内容粘贴入textarea的动作,而当使用js间接提交表单时,kindeditor的这个自动的动作没有被触发,所以

后台得到的是还未被粘贴内容的textarea的文本。

3、解决:最直接的想办法是手动调一下这个自动粘贴的程序。搜索了一下发现已有人解决。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>

上一篇:Adobe Illustrator for Mac(矢量图处理软件)破解版安装


下一篇:【矢量绘图工具】Adobe Illustrator (AI) CC 2019 for Mac 23.0