springboot集成editor.md开发markdonw编辑器
作者:互联网
参考:https://www.jianshu.com/p/9d245837aa4c
1、下载开源editor.md在线markdown编辑器,
下载地址:http://editor.md.ipandao.com/
下载完成后,解压,
2、在springboot项目中的static中创建如下文件夹:
3、复制解压文件editor.md-master中的相应文件到springboot项目中
examples文件中的css、js、images全部复制
lib中的所有文件
css文件夹中的editormd.css , editormd.preview.css文件
fonts中的所有文件
images文件夹中的loading.gif
根目录下的editormd.js , editormd.min.js
springboot目录如下:
4、复制examples文件夹中的simple.html , html-preview-markdown-to-html.html到springboot项目
改名为:
simple.html ——edit.html
html-preview-markdown-to-html.html——view.html
5、修改edit.html页面
首先修改js/css引入路径
修改lib路径
效果:
添加按钮:
/**下述为新增,上面一行记得加逗号结束*/
/*指定需要显示的功能按钮*/
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 : "<span bgcolor=\"gray\">发布</span>",
index : "<span bgcolor=\"red\">返回首页</span>",
},
/*给自定义按钮指定回调函数*/
toolbarHandlers:{
releaseIcon : function(cm, icon, cursor, selection) {
contentCommit();//提交表单代码在下面
console.log("日志输出 testIcon =>", this, cm, icon, cursor, selection);
},
index : function(){
window.location.href = '返回首页的路径.html';
},
}
效果:
添加form表单:
<form name="mdEditorForm">
<div id="test-editormd">
<textarea style="display: none;">正文内容
</textarea>
</div>
</form>
提交form表单:
/*提交表单的js*/
function contentCommit(){
mdEditorForm.method = "post";
mdEditorForm.action = "contentCommit";//提交至服务器的路径
mdEditorForm.submit();
}
完整代码:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8" />
<title>Simple example - Editor.md examples</title>
<link rel="stylesheet" href="/editormd/css/style.css" />
<link rel="stylesheet" href="/editormd/css/editormd.css" />
<link rel="shortcut icon"
href="https://pandao.github.io/editor.md/favicon.ico"
type="image/x-icon" />
</head>
<body>
<div id="layout">
<header>
<h1>标题</h1>
</header>
<form name="mdEditorForm">
<div id="test-editormd">
<textarea style="display: none;">正文内容
</textarea>
</div>
</form>
</div>
<script src="/editormd/js/jquery.min.js"></script>
<script src="/editormd/js/editormd.min.js"></script>
<script type="text/javascript">
var testEditor;
$(function() {
testEditor = editormd("test-editormd", {
width : "90%",
height : 640,
syncScrolling : "single",
path : "/editormd/lib/",
/**下述为新增,上面一行记得加逗号结束*/
/*指定需要显示的功能按钮*/
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 : "<span bgcolor=\"gray\">发布</span>",
index : "<span bgcolor=\"red\">返回首页</span>",
},
/*给自定义按钮指定回调函数*/
toolbarHandlers:{
releaseIcon : function(cm, icon, cursor, selection) {
contentCommit();//提交表单代码在下面
console.log("日志输出 testIcon =>", this, cm, icon, cursor, selection);
},
index : function(){
window.location.href = '返回首页的路径.html';
},
}
});
/*
// or
testEditor = editormd({
id : "test-editormd",
width : "90%",
height : 640,
path : "../lib/"
});
*/
});
/*提交表单的js*/
function contentCommit(){
mdEditorForm.method = "post";
mdEditorForm.action = "contentCommit";//提交至服务器的路径
mdEditorForm.submit();
}
</script>
</body>
</html>
相应Controller方法
@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;
}
6、修改view.html
修改css、js引入路径
修改
完整代码
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8" />
<title>HTML Preview(markdown to html) - Editor.md examples</title>
<link rel="stylesheet" href="/editormd/css/style.css" />
<link rel="stylesheet" href="/editormd/css/editormd.preview.css" />
<link rel="shortcut icon" href="https://pandao.github.io/editor.md/favicon.ico" type="image/x-icon" />
<style>
.editormd-html-preview {
width: 90%;
margin: 0 auto;
}
</style>
</head>
<body>
<div id="layout">
<header>
<h1>Markdown转HTML的显示处理</h1>
<p>即:非编辑情况下的HTML预览</p>
<p>HTML Preview(markdown to html)</p>
</header>
<div class="btns">
<button onclick="location.href='./html-preview-markdown-to-html-custom-toc-container.html';">将 ToC 移到自定义容器层</button>
</div>
<div id="test-editormd-view">
<textarea style="display:none;" name="test-editormd-markdown-doc">###Hello world!</textarea>
</div>
<div id="test-editormd-view2">
<textarea id="append-test" style="display:none;">
</textarea>
</div>
</div>
<!-- <script src="js/zepto.min.js"></script>
<script>
var jQuery = Zepto; // 为了避免修改flowChart.js和sequence-diagram.js的源码,所以使用Zepto.js时想支持flowChart/sequenceDiagram就得加上这一句
</script> -->
<script src="/editormd/js/jquery.min.js"></script>
<script src="/editormd/lib/marked.min.js"></script>
<script src="/editormd/lib/prettify.min.js"></script>
<script src="/editormd/lib/raphael.min.js"></script>
<script src="/editormd/lib/underscore.min.js"></script>
<script src="/editormd/lib/sequence-diagram.min.js"></script>
<script src="/editormd/lib/flowchart.min.js"></script>
<script src="/editormd/lib/jquery.flowchart.min.js"></script>
<script src="/editormd/js/editormd.js"></script>
<script type="text/javascript">
$(function() {
var testEditormdView, testEditormdView2;
$.get("getContent", function(markdown) {
testEditormdView = editormd.markdownToHTML("test-editormd-view", {
markdown : markdown ,//+ "\r\n" + $("#append-test").text(),
//htmlDecode : true, // 开启 HTML 标签解析,为了安全性,默认不开启
htmlDecode : "style,script,iframe", // you can filter tags decode
//toc : false,
tocm : true, // Using [TOCM]
//tocContainer : "#custom-toc-container", // 自定义 ToC 容器层
//gfm : false,
//tocDropdown : true,
// markdownSourceCode : true, // 是否保留 Markdown 源码,即是否删除保存源码的 Textarea 标签
emoji : true,
taskList : true,
tex : true, // 默认不解析
flowChart : true, // 默认不解析
sequenceDiagram : true, // 默认不解析
});
//console.log("返回一个 jQuery 实例 =>", testEditormdView);
// 获取Markdown源码
//console.log(testEditormdView.getMarkdown());
//alert(testEditormdView.getMarkdown());
});
testEditormdView2 = editormd.markdownToHTML("test-editormd-view2", {
htmlDecode : "style,script,iframe", // you can filter tags decode
emoji : true,
taskList : true,
tex : true, // 默认不解析
flowChart : true, // 默认不解析
sequenceDiagram : true, // 默认不解析
});
});
</script>
</body>
</html>
Controller方法
/**
* 读取所保存的markdown数据
* @return
*/
@RequestMapping("getContent")
@ResponseBody
public String getContent(){
return "### 这是markdown格式的内容,暂时<i>固定</i>写死,应从数据库读取上个接口保存的内容";
}
完整Controller
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
@Controller
@RequestMapping
public class BlogController {
@Autowired
private BlogMapper blogMapper;
@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;
}
/**
* 读取所保存的markdown数据
* @return
*/
@RequestMapping("getContent")
@ResponseBody
public String getContent(){
return "### 这是markdown格式的内容,暂时<i>固定</i>写死,应从数据库读取上个接口保存的内容";
}
}
【纯文本内容编辑基本完成,带图片上传往下】
7、剪切板图片上传
目前只能读取QQ等应用Ctrl+Alt+A截图的图片,无法读取在桌面右键->复制的图片
修改edit.html中的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);
});
//执行上传
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){
}
});
}
edit.html完整代码
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8" />
<title>Simple example - Editor.md examples</title>
<link rel="stylesheet" href="/editormd/css/style.css" />
<link rel="stylesheet" href="/editormd/css/editormd.css" />
<link rel="shortcut icon"
href="https://pandao.github.io/editor.md/favicon.ico"
type="image/x-icon" />
</head>
<body>
<div id="layout">
<header>
<h1>标题</h1>
</header>
<form name="mdEditorForm">
<div id="test-editormd">
<textarea style="display: none;">正文内容
</textarea>
</div>
</form>
</div>
<script src="/editormd/js/jquery.min.js"></script>
<script src="/editormd/js/editormd.min.js"></script>
<script type="text/javascript">
var testEditor;
$(function() {
testEditor = editormd("test-editormd", {
width : "90%",
height : 640,
syncScrolling : "single",
path : "/editormd/lib/",
/**下述为新增,上面一行记得加逗号结束*/
/*指定需要显示的功能按钮*/
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 : "<span bgcolor=\"gray\">发布</span>",
index : "<span bgcolor=\"red\">返回首页</span>",
},
/*给自定义按钮指定回调函数*/
toolbarHandlers:{
releaseIcon : function(cm, icon, cursor, selection) {
contentCommit();//提交表单代码在下面
console.log("日志输出 testIcon =>", this, cm, icon, cursor, selection);
},
index : function(){
window.location.href = '返回首页的路径.html';
},
}
});
/*
// or
testEditor = editormd({
id : "test-editormd",
width : "90%",
height : 640,
path : "../lib/"
});
*/
//读取剪切板
$("#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);
});
});
//执行上传
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*/
function contentCommit(){
mdEditorForm.method = "post";
mdEditorForm.action = "contentCommit";//提交至服务器的路径
mdEditorForm.submit();
}
</script>
</body>
</html>
controller方法
图片在这里没有存储在数据库中,而是直接写在了本地服务器能够访问的地方
/**
* 上传文件
* @return
*/
@RequestMapping("uploadMdFile.json")
@ResponseBody
public Map<String,Object> getContent(HttpServletRequest request,@RequestParam(value = "file",required=false) MultipartFile file,String topicCode){
Map<String,Object> 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<String, MultipartFile> multipartFileMap = multipartRequest.getFileMap();
for (Map.Entry<String, MultipartFile> 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:8081/"+fileName);
//上传到文件服务器路径,此处我直接上传到项目部署编译路径,需要调整
OutputStream os = new FileOutputStream(new File("D:\\Workspaces\\uploaddemo3\\target\\classes\\static" , fileName));
os.write(b);
os.flush();
}
}
}catch (Exception e){
}
return resultMap;
}
controller完整代码
package com.example.uploaddemo3;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
@Controller
@RequestMapping
public class BlogController {
@Autowired
private BlogMapper blogMapper;
@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;
}
/**
* 读取所保存的markdown数据
* @return
*/
@RequestMapping("getContent")
@ResponseBody
public String getContent(){
return "### 这是markdown格式的内容,暂时<i>固定</i>写死,应从数据库读取上个接口保存的内容";
}
/**
* 上传文件
* @return
*/
@RequestMapping("uploadMdFile.json")
@ResponseBody
public Map<String,Object> getContent(HttpServletRequest request,@RequestParam(value = "file",required=false) MultipartFile file,String topicCode){
Map<String,Object> 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<String, MultipartFile> multipartFileMap = multipartRequest.getFileMap();
for (Map.Entry<String, MultipartFile> 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:8081/"+fileName);
//上传到文件服务器路径,此处我直接上传到项目部署编译路径,需要调整
OutputStream os = new FileOutputStream(new File("D:\\Workspaces\\uploaddemo3\\target\\classes\\static" , fileName));
os.write(b);
os.flush();
}
}
}catch (Exception e){
}
return resultMap;
}
}
效果
标签:function,md,springboot,html,editormd,topicCode,markdonw,import,data 来源: https://blog.csdn.net/weixin_43653670/article/details/112094372