背景
最近项目中可能要支持markdown 编辑器,所以提前调研了一下;现在基本做技术的没有不知道Markdown 的,因为实在是太强大了,只需要了解很简单的几个操作,即可编辑非常优美的文章,包括TeX科学公式(基于KaTeX)、流程图 Flowchart 和 时序图 ,不在让你浪费时间在格式的调整。
资源:Editor.md https://pandao.github.io/editor.md/
这个插件就不多说了,以为官网已经说的很多了,虽然在使用上有点小瑕疵,但是大体上还是很不错的,即插即用。
editor.md目录介绍
这边需要说明一下 editor.md目录,本文介绍的版本为v1.5.0,在首页下载完成,解压editor.md-master.zip文件,可以看到如下图的目录结构:
图中红色框内是我们要引用到项目的文件和目录。
- css目录中可选择editormd.min.css放在对应的项目css目录中;
- js可选择editormd.min.js放置在对应项目的js目录中,需要注意的是同时需要引入jQuery,这里使用jquery.min.js;
- examples文件夹中是一部分核心功能的demo,在使用的过程中用到对应的组件或功能可打开参考;
- fonts是需要用到字体,可一并引入项目;
- images是一些加载类的图片;
- lib是editor.md依赖的第三方js资源,比如流程图的js资源;
- plugins主要是编辑器上面的操作功能插件,比如图片上传等,可选择使用的进行加载;
导入到web 项目中的目录如下:
红线框内 fonts,images,lib,plugins 目录是从解压的文件中原封不动拷贝过来的,尽量不改动(文件夹名字),因为后面如果要引入会出现奇葩的问题。
红线框内js,css 文件,我只拷贝了editormd.js,editormd.css
引入css和js
在使用到editor.md的页面引入css和js:
<link href="#springUrl('/static/js/plugins/editormd/css/editormd.css')" rel="stylesheet">
<script src="#springUrl('/static/js/jquery.min.js')"></script>
<script src="#springUrl('/static/js/plugins/editormd/js/editormd.js')"></script>
页面中添加div
在需要添加editor.md编辑器的地方输入以下div:
<div id="test-editormd">
<textarea style="display:none;" class="form-control" id="content-editormd-markdown-doc" name="content-editormd-markdown-doc">
</textarea>
</div>
- id为content-editormd,后面的js代码中需要用到。
- 通过form表单提交时后台可通过content-editormd-markdown-doc获取到对应的markdown文档内容。比如Java中可通过request.getParameter(“content-editormd-markdown-doc”)
注意:此处需要注意的是,无论需要html格式的内容还是markdown格式的内容,都只需要写一个textarea。此处有一个很大的坑。不少其他教程中说需要两个textarea,那么会导致后一个textarea后台获得的数据是一个数组,而不是单纯的HTML内容。
页面添加js代码
js代码用来将上面的textarea进行渲染:
<script type="text/javascript">
$(function() {
var editor = editormd("test-editormd", {
width : "90%",
height : 720,
theme : "dark",
path : "#springUrl('static/js/plugins/editormd/lib/')",
saveHTMLToTextarea : true, // 保存 HTML 到 Textarea
toolbarAutoFixed:true,//工具栏自动固定定位的开启与禁用
});
});
</script>
此段js代码中的content-editormd就是上面div的id。
path路径需要指定到项目中对应的lib的路径。如果设置不对markdown 无法渲染出来。
saveHTMLToTextarea设置为true表示,转化为html格式的内容也同样提交到后台。
好,到这边你就可以看到页面效果了。如下:
上传图片
editor.md的上传图片功能实现起来比较简单,只需要在上段代码中再添加一些配置即可。
<script type="text/javascript">
$(function() {
var editor = editormd("test-editormd", {
width : "90%",
height : 720,
theme : "dark",
path : "#springUrl('static/js/plugins/editormd/lib/')",
saveHTMLToTextarea : true, // 保存 HTML 到 Textarea
toolbarAutoFixed:true,//工具栏自动固定定位的开启与禁用
imageUpload : true,
imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
imageUploadURL : "/file/uploadImage"
});
});
</script>
- imageUpload设置为true表示支持上传,此时需要plugins中的image-dialog.js插件(默认调用,放在指定路径即可)。
- imageFormats为支持上传的图片类型。
- imageUploadURL要上传图片的后台服务器路径
这个时候,你就可以看到,但你点击工具栏上“添加图片” 的时候。 效果如下:
但是,当你点击“本地上传” 后,发现没有任何反映。图片地址也没有回填;我第一反应是是不是我参数设置有问题啊,但是,经过我看demo 包括,上git 讨论区查看此类问题的时候,并没有,就只需要设置imageUpload,imageFormats,imageUploadURL 三个参数即可,除非跨域了;但是我的情况并没有跨域啊,所以我打开查看页面的调试模式,看到了一个错,报;如图:
这尼玛什么情况。。。;完全摸不着头脑。
再到后台一看,已经报错。
意思就是,后台接口中声明了MultipartFile ,所以必须要加入:
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.2</version>
</dependency>
同时必须在spring-web.xml 中加入,注意:id 必须是multipartResolver
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize">
<value>5242880</value></property>
</bean>
然后重启一下,再次点击“本地上传”,
已经可以了。
到了这一步,我们再来深究一下这个弹框,本地上传到底做了什么,看看源码来。贴上源码
一看,这他娘什么啊,从uploadIframe获取内容 ,还解析,把url 赋值到data-url 上;这是什么鬼啊,再回去看看创建这个dialog 做了哪些事情。
看到这边,恍然大悟没有。。。,
原来它在这个“本地上传”按钮 做的事情,不仅仅是选取了本地的图片资源,而且还请求上传到服务后端了,而且表单提交是在iframe 里面执行的,所以才有了上面解析iframe 内容的代码,如果后端没有反应,或者返回的格式有问题,你就根本不知道发生了什么了,哎,坑。教程也没有说明一下。
特地说明一下:后端返回的报文必须json 格式:
{
success : 0 | 1, //0表示上传失败;1表示上传成功
message : "提示的信息",
url : "图片地址" //上传成功时才返回
}
0,1 必须是数字;
url :就是你图片存在的地址,这个就是返回到弹框中的图片地址。
这边附上后端上传图片代码:
@Controller
@RequestMapping("/file")
public class FileUploadController {
private static final Logger LOGGER = LoggerFactory.getLogger(FileUploadController.class);
private static final String IMAGE_URL_PRE ="http://access-usf.jd.com:8089/file/viewImage?key=";
@Resource
private MessageStoreService messageStoreService;
@ResponseBody
@RequestMapping("/uploadImage")
public String uploadImage(HttpSession session, @RequestParam(value = "editormd-image-file") MultipartFile file) {
LOGGER.info("========================上传图片开始=========================");
JSONObject res = new JSONObject();
try {
byte[] fileByte = file.getBytes();
String fileName = file.getOriginalFilename();
String key = messageStoreService.saveBytes(fileByte);
if(StringUtils.isNotEmpty(key)){
res.put("success", 1);
res.put("message", "上传成功");
res.put("url",IMAGE_URL_PRE+key);
} else {
res.put("success", 0);
res.put("message", "上传失败" );
}
} catch (Exception e) {
LOGGER.error("上传图片异常", e);
res.put("success", 0);
res.put("message", "上传异常");
}
LOGGER.info("上传图片返回结果:{}", res);
return res.toString();
}
/**
在线预览图片
*/
@RequestMapping("/viewImage")
@ResponseBody
public void viewImage(HttpServletRequest request, HttpServletResponse response, String key){
BufferedInputStream bis = null;
OutputStream os = null;
response.setContentType("text/html; charset=UTF-8");
response.setContentType("image/jpeg");
byte[] file = messageStoreService.loadBytes(key);
try {
os = response.getOutputStream();
os.write(file);
os.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (os != null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bis != null){
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
好辣,看一下效果。
已经上传成功了。。。