前言
本篇主要是使用Java语言结合开源editor.md进行开发markdown论坛功能,具体请看下图,如果不是你想要的,那可以看我另外一篇使用commonmark-java解析成html文档的文章
详细功能:
markdown界面很简洁,也就是这样,下图是你想要的吗?
原材料准备
editor.md
是国人开发的开源在线Markdown编辑器,单纯基于前端JavaScript,无需后台代码加持,适用于任何语言,我这里使用examples示例来进行改造,很久没更新了.....
首页地址:http://pandao.github.io/editor.md/
项目搭建,资源准备
注意:这个步骤过程有点繁琐,尤其是一些资源文件的访问,所以可以根据自己的情况进行调整,但一定要看到第10步对应的界面,资源准备才算完成
- 名词:下述所有的“外层”表示源码包的第一层目录
- 新建SpringBoot项目,这里不做赘述,不会的朋友请看超简单SpringBoot搭建
- 然后将外层examples文件夹中的css、js资源,拷贝到resources下的static中
- 将外层examples文件夹中的simple.html示例文件拷贝到工程的templates下面,如下图
- 编辑simple.html文件,将如下图的资源文件路径根据你的项目进行调整
-
上图中的editormd.css和editormd.min.js需要从外层文件中拷贝进来,拷贝进来后如下图
-
拷贝外层的lib目录
-
拷贝外层的fonts目录,并且将外层文件夹images中的loading.gif拷贝到我们项目的images中
-
最终 调整后的simple.html文件内容为
-
加入Controller类,以便于做后续操作
-
启动服务器,要求能够访问simple.html,并且样式能够正常加载没有错误,如下图
开始开发,前方高能
开发主要分为3个层面
- 简单的后台提交内容示例。
- 前台接收markdown内容页面显示时进行转换加载。
- 复杂的服务器端开发(带文件上传)
1、后台内容提交页面
该页面即simple.html页面,核心的加载的JS在代码在页面底部,如下:
var testEditor;
$(function() {
testEditor = editormd("test-editormd", {
width : "90%",
height : 640,
syncScrolling : "single",
path : "lib/"
});
});
开发提交功能
开始进入简单功能的开发,其JS的写法,也可以看examples中其他页面,对应不同的功能,有不同的JS,或者直接跟我进入开发,保证不带沟里
- simple界面中,加入form表单,修改JS,简单的提交客户端完成,内容如下:
/**下述为新增,上面一行记得加逗号结束*/
/*指定需要显示的功能按钮*/
toolbarIcons : function() {
return ["undo", "redo", "|","bold", "italic","ucwords","uppercase","lowercase","|","h1","h2","h3","h4","h5","h6","|","list-ul","list-ol","table","datetime","hr", "||", "watch", "fullscreen", "preview", "releaseIcon", "index"]
},
/*自定义功能按钮,下面我自定义了2个,一个是发布,一个是返回首页*/
toolbarIconTexts : {
releaseIcon : "发布",
index : "返回首页",
},
/*给自定义按钮指定回调函数*/
toolbarHandlers:{
releaseIcon : function(cm, icon, cursor, selection) {
contentCommit();//提交表单代码在下面
console.log("日志输出 testIcon =>", this, cm, icon, cursor, selection);
},
index : function(){
window.location.href = '返回首页的路径.html';
},
}
另外需要提供提交JS的代码
/*提交表单的js*/
function contentCommit(){
mdEditorForm.method = "post";
mdEditorForm.action = "contentCommit";//提交至服务器的路径
mdEditorForm.submit();
}
-
修改Controller,加入接收提交内容的方法,并且提供一个简单的view界面,用于预览,如下
上图代码如下:
@Controller
@RequestMapping
public class MarkdownController {
@RequestMapping("simple")
public ModelAndView simple(){
//这里的文件名将直接定位到templates下面的文件
ModelAndView modelAndView = new ModelAndView("simple");
System.out.println("进入内容编写页面");
return modelAndView;
}
@RequestMapping("contentCommit")
public ModelAndView contentCommit(String content){
System.out.println("提交的内容为:" + content);
ModelAndView modelAndView = new ModelAndView("view");
//将内容发送至前台预览
modelAndView.addObject("viewContent" , content);
System.out.println("跳转至内容显示页面");
return modelAndView;
}
}
- 启动服务器,并且点击提交,你会发现内容正确到达客户端。
简单的提交完成,接下来,开发预览界面
2. 前台加载markdown语法,并且解析
-
拷贝examples中的html-preview-markdown-to-html.html页面,改名为view.html界面,将其中依赖的静态资源文件全部调整正确, 并且访问不报错误,所以你需要将不存在的文件有序的拷贝进来
- 新依赖外层css中的editormd.preview.css文件
- 新依赖外层的editormd.js,或直接将内容改为editormd.min.js,两者效果一样
- 新依赖examples中的test.md文件,jquery.min.js所依赖,可以修改jquery文件,移除掉
更改完成后的内容页面如下,控制台不能够报错
预览界面重要代码如下
- 前台页面textarea id="append-test"中的内容全部清除,将上图JS中的test.md改为内容请求地址,后台提供一个rest内容获取接口
/**
* 读取所保存的markdown数据
* @return
*/
@RequestMapping("getContent")
@ResponseBody
public String getContent(){
return "### 这是markdown格式的内容,暂时固定写死,应从数据库读取上个接口保存的内容";
}
到此为止,简单的markdonw集成进来了,其中的按钮以及js用法,还需要结合其他的页面进行进一步探讨,整体流程效果图如下:
3. 剪切板文件上传代码加入
- 很多时候我们上传文件不会手动去写markdown的文件语法,也不会去点击markdown提供的文件上传按钮,更多的是通过Ctrl+C、Ctrl+V来读取剪贴板实现上传,类似于、csdn等博客都有实现该功能。但在如下代码中,剪切板的读取存在浏览器兼容的问题,经测试,目前只能读取QQ等应用Ctrl+Alt+A截图的图片,无法读取在桌面右键->复制的图片,望各位悉知,效果图如下
-
编写JS代码
//读取剪切板
$("#test-editormd").on('paste', function (ev) {
var topicCode = $("#topicCode").val();
//详细可查看clipboardData属性的使用方式
var data = (ev.clipboardData || ev.originalEvent.clipboardData);
var items1 = data.items;
console.log(items1);//输出 DataTransferItem对象
var imageFile;
for(var index in items1){
var item = items1[index];
//如果kind是file,可以用getAsFile()方法获取到文件
if (item.kind === 'file') {
imageFile = item.getAsFile();
console.log('获取到剪贴板的文件' + item.kind);
break;
}else if(item.kind === 'string'){
console.log('获取到剪贴板的字符串' + item.kind);
}
}
//执行上传
uploadTrigger(imageFile,topicCode);
});
上图中uploadTrigger(imageFile,topicCode)
函数为文件上传的js代码,如下:
//执行上传
function uploadTrigger(imageFile,topicCode){
//topicCode为文章代码,需要在关联图片,从而实现预览时准确加载到图片
var url = "uploadMdFile.json?topicCode="+topicCode;
var formdata = new FormData();
formdata.append("file", imageFile);
$.ajax({
url: url,
type: "post",
data: formdata,
//关闭序列化
processData: false,
contentType: false,
success: function (data) {
//data为后台返回的retMap数据
if(data.retCode == "success"){
//向markdown区域插入该格式的值,从而实现图片在右侧显示
testEditor.insertValue("\n![" + data.file + "](" + data.rootPath + ")");
} else {
console.log(data.msg);
}
},
error : function(data){
}
});
}
- 页面JS完成后,需要实现后台服务端接收并存储图片,代码片段如下,图片我这里没有存储在数据库中,而是直接写在了本地服务器能够访问的地方,可根据实际情况进行改变目录或者上传至OSS存储服务器
/**
* 上传文件
* @return
*/
@RequestMapping("uploadMdFile.json")
@ResponseBody
public Map getContent(HttpServletRequest request,@RequestParam(value = "file",required=false) MultipartFile file,String topicCode){
Map resultMap = new HashMap<>();
if(topicCode != null && !"".equals(topicCode)){
//该CODE用于对应图片存储,实际项目中需要存储该文章与图片的关系,我这不做处理
System.out.println("主题CODE->" + topicCode);
}
try {
// 检测是不是存在上传文件
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if(isMultipart){
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
Map multipartFileMap = multipartRequest.getFileMap();
for (Map.Entry entryFile : multipartFileMap.entrySet()) {
MultipartFile value = entryFile.getValue();
//读取输入流
InputStream is = value.getInputStream();
//获取文件名
String fileName = value.getOriginalFilename();
//声明byte缓冲数组
byte[] b = new byte[(int) value.getSize()];
is.read(b);
//将文件名上传的name作为返回的key,默认为file
resultMap.put(entryFile.getKey() , fileName);
//返回接口调用状态码
resultMap.put("retCode" , "success");
//返回图片访问路径,此处可以改为OSS分布式存储,根据项目具体情况调整
resultMap.put("rootPath" , "http://localhost:8080/"+fileName);
//上传到文件服务器路径,此处我直接上传到项目部署编译路径,需要调整
OutputStream os = new FileOutputStream(new File("F:\\work\\testjava\\md\\target\\classes\\static" , fileName));
os.write(b);
os.flush();
}
}
}catch (Exception e){
}
return resultMap;
}
上述代码中 F:\\work\\testjava\\md\\target\\classes\\static
指的是我的代码编译目录,该目录下的文件可以通过:http://localhost:8080/xxx
进行访问,请根据自身情况进行调整
先暂时更新到这里,具体的功能基本已经实现,若需要读取桌面文件,则可能需要实现文本框也支持图片流,可以采用H5的FileReader实现,后续再更....