其他分享
首页 > 其他分享> > springboot集成editor.md开发markdonw编辑器

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