springboot集成editor.md进行markdown文档的编写及查看


一、Editor.md简介

Editor.md 是一款开源的、可嵌入的 Markdown 在线编辑器(组件),基于 CodeMirror、jQuery 和 Marked 构建

二、与springboot集成过程

本文为springboot与editor.md的集成例子,详细说明如何在springboot工程中使用editor.md进行markdown文档的编写,包含前端页面与后台数据库及接口,例子很完整,建好库表后,启动工程即可测试。

2.1springboot后台部分的准备

2.1.1 数据库建表

建立一张文章表用来存储markdown文章:

DROP TABLE IF EXISTS `article`;
CREATE TABLE `article`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `title` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '文章名称',
  `content` longtext CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '文章md的内容',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 17 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

2.1.2 springboot添加mybatis

略,可详参考本文最后的源码里面查看

2.1.3 springboot对article表进行增删改查

public interface ArticleService {
	//查看所有文章列表
	public List<Article> getArticleList();
    //添加文章
    public int addArticle(Article article);
    //更新文章
    public int updateArticle(Article article);
    //删除文章
    public int deleteArticle(Article article);
    //根据文章id查询文章
    public Article getArticleById(Integer id);
}

另外ArticleController类中增加一个文件上传接口用来进行图片的上传,上传至临时目录
还有两个查看图片的接口(查看临时目录下的图片,和查看正式目录下的图片)

	//临时照片展示
    @GetMapping("/getTempImage")
	
	//正式照片展示,md上传的新照片,是先以/getTempImage?fileName=xxx的方式临时存放
    //发表文章或修改文章时,通过以文章Id为目录将文章的图片独立出去管理,
    //正式的照片访问url如:/getImage?id=10&fileName=xxxxxxxxxxxx.jpg
    @GetMapping("/getImage")
     
     //上传图片并回显
    @ResponseBody
    @RequestMapping("/article/uploadImg")

注:
md上传的新照片,是先以/getTempImage?fileName=xxx的方式临时存放,等发表文章或修改文章时,通过以文章Id为目录将文章的图片独立出去管理,l如:/getImage?id=10&fileName=xxxxxxxxxxxx.jpg,此逻辑在ArticleServiceImpl类的addArticle和updateArticle方法中都有相应的实现代码,如:

	@Override
	@Transactional
	public int addArticle(Article article) {
		
		try {
			//先插入数据库
			articleMapper.addArticle(article);
			
			//取出自增id
			int id = article.getId();
			
			//将文章内容中的临时图片转换成正式的图片访问地址
			String articleContentTemp = article.getContent();
			
			//重新更新进去,articleContent.replaceAll后不会改变articleContent的值,在后面会继续进行临时图片的查询并替换
			article.setContent(article.getContent().replaceAll("/getTempImage\\?fileName=", "/getImage\\?id="+id+"&fileName="));
			articleMapper.updateArticle(article);
			
			//============================
			//查找文章内容中临时的图片,在文章保存(新增或修改)到数据库时,同时将临时目录下的图片迁移到以文章Id为目录的图片文件夹
			String tempImagePattern = "(getTempImage\\?fileName=)(\\w+-\\w+-\\w+-\\w+-\\w+\\.\\w+)(.*)";
			Pattern r = Pattern.compile(tempImagePattern);
			Matcher m = r.matcher(articleContentTemp);

			boolean imageDirCreted = false;
			while(m.find()){
				
				if(!imageDirCreted){
					Path newRealPath = Paths.get(imageAbsoluteFilePath+"/"+imageUploadPathPrex+"/"+id);
					//创建文件夹(不存在则创建)
					Files.createDirectories(newRealPath);		
					imageDirCreted = true;
				}
				
				String tempImageFileName = m.group(2);
			    try {
			    	Path fileRealPath = Paths.get(imageAbsoluteFilePath+"/" + imageUploadPathPrex + "/" + tempPrex + "/"+tempImageFileName);
			        Path newRealPathOfFile = Paths.get(imageAbsoluteFilePath+"/" + imageUploadPathPrex + "/"+id+"/"+tempImageFileName);
			        //覆盖式地复制文件
			        //Files.copy(fileRealPath, newRealPathOfFile, StandardCopyOption.REPLACE_EXISTING);
			        
			        //将临时图片移至正式目录(以文章id命名的目录)
			        logger.info("将临时图片移至正式目录,{},fileName:{}",id,tempImageFileName);
			        Files.move(fileRealPath, newRealPathOfFile);
			        
			        
			    } catch (IOException e) {
			        throw new RuntimeException("重命名文件失败");
			    }
			    
			}
			
			//最后,比对正式目录下的所有图片,将一些垃极图片从正式目录中也删除
			String tempImagePattern2 = "(getImage\\?id=\\d+&fileName=)(\\w+-\\w+-\\w+-\\w+-\\w+\\.\\w+)(.*)";
			Pattern r2 = Pattern.compile(tempImagePattern2);
			Matcher m2 = r2.matcher(article.getContent());
			HashSet<String> imageFileNameSet = new HashSet<String>();
			while(m2.find()){
				imageFileNameSet.add(m2.group(2));
			}
			File dir = new File(imageAbsoluteFilePath+"/"+imageUploadPathPrex+"/"+id);
			if(dir.exists()){
				String[] fileNames = dir.list();
				List<String> needDeleteFiles = new ArrayList<String>();
				for(String fileName : fileNames) {
					if(!imageFileNameSet.contains(fileName)){
						needDeleteFiles.add(fileName);
					}
				}
				for(String needDeleteFileName : needDeleteFiles){
					File file = new File(dir, needDeleteFileName);
					//从正式目录删除无效文件
			        logger.info("从正式目录删除无效文件,{},fileName:{}",id,needDeleteFileName);
					boolean success = file.delete();
					if(!success){
						throw new RuntimeException("删除无效文件失败:"+ needDeleteFileName);
					}
				}				
			}
			
			
		} catch (Exception e) {
			logger.error("error",e);
			throw new RuntimeException(e.getMessage());
			
		} finally {
			
		}
		
		return article.getId();
	}

2.2editor.md搭建过程

2.2.1editor.md下载

官网地址:https://pandao.github.io/editor.md/en.html
或我的网盘分享:

链接:https://pan.baidu.com/s/1Dl62ld1u6p5Z8ZNTijWhxA
提取码:2zan

2.2.2editor.md资源文件准备

在springboot工程中新建static文件目录(如果没有的话)然后将下载压缩包中的相应文件复制进去。
在这里插入图片描述
另外editormd.js相关的我新建了一个新的js目录来存放,
在这里插入图片描述

2.2.3编写相关前端页面

这里我写了三个页面articleList.html(文章列表)previewArticle.html(文章查看)showEditor.html(markdown编辑)

编辑器的构造代码如:

testEditor = editormd("test-editormd", {
    width   : "100%",
    height  : 640,
    syncScrolling : "single",
    path    : contextPath + "/lib/",
    imageUpload: true, //同意图片上传
    imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
    imageUploadURL: contextPath +"/article/uploadImg",       //图片上传URL 即后台的图片上传接口URL
    onload : function() {
        
    }
});

在这里插入图片描述

浏览文章代码如:

var 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,  // 默认不解析
});

2.2.4界面测试效果

测试地址:http://localhost:8080/articleList.html

文章列表(没什么样式)
在这里插入图片描述
编辑文章或发表新文章
在这里插入图片描述
发布后浏览的示例效果如
在这里插入图片描述
动态图:
在这里插入图片描述

三、部分问题解决

3.1使用editormd时候katex.min.css加载超慢

由于他引用了国外的cdn导致加载变慢甚至不能加载的情况,我们需要修改到自己的服务器路径

http://micuer.com/static/js/katex.min.css
http://micuer.com/static/js/katex.min.js

下载好上述2个文件后,修改文件editormd.js大约4181行

    // 使用国外的CDN,加载速度有时会很慢,或者自定义URL
    // You can custom KaTeX load url.
    editormd.katexURL  = {
        css : "css/katex.min",
        js  : "js/katex.min"
    };

3.2Editor.md编辑器默认上传的图片太小不能控制解决

无法手动控制图片大小,默认是最大的,有时需要想自己设置大小比如 ![图片描述](url =300x150)

首先找到lib目录中的marked.min.js这个文件
然后定位Renderer.prototype.image 这个就是生成图片html的位置,原代码如:

Renderer.prototype.image = function(href, title, text) {
            var out = '<img src="' + href + '" alt="' + text + '"';
            if (title) {
                out += ' title="' + title + '"'
            }
            out += this.options.xhtml ? "/>" : ">";
            return out
        }

修改后:

Renderer.prototype.image = function(href, title, text) {
            var array = href.split("=");
            var width;
            var height;
            if(array.length == 2){
                href = array[0];
                var resolution = array[1].split("x");
                if (resolution.length == 2){
                    width = resolution[0]
                    height = resolution[1];
                }
            }
            var out = '<img src="' + href + '" alt="' + text + '"';
            if (title) {
                out += ' title="' + title + '"'
            }
            if(width){
                out += ' width="' + width + '"'
            }
            if(height){
                out += ' height="' + height + '"'
            }
            out += this.options.xhtml ? "/>" : ">";
            return out
        };

修改后,通过在线的Js压缩工具进行js压缩后,替换到marked.min.js即可

3.3Editor.md添加支持复制图片并直接粘贴

需要支持截图并粘贴的话,可采用插件扩展的方式
新建一个uploadImage.js,将以下代码内容如下复制进去:

function initPasteDragImg(Editor){
   var doc = document.getElementById(Editor.id);
   doc.addEventListener('paste', function (event) {
       var items = (event.clipboardData || window.clipboardData).items;
       var file = null;
       if (items && items.length) {
           // 搜索剪切板items
           for (var i = 0; i < items.length; i++) {
               if (items[i].type.indexOf('image') !== -1) {
                   file = items[i].getAsFile();
                   break;
               }
           }
       } else {
           //console.log("当前浏览器不支持");
           return;
       }
       if (!file) {
           //console.log("粘贴内容非图片");
           return;
       }
       //console.log(file);
       uploadImg(file,Editor);
   });

   var dashboard = document.getElementById(Editor.id)
   dashboard.addEventListener("dragover", function (e) {
       e.preventDefault()
       e.stopPropagation()
   })
   dashboard.addEventListener("dragenter", function (e) {
       e.preventDefault()
       e.stopPropagation()
   })
   dashboard.addEventListener("drop", function (e) {
       e.preventDefault()
       e.stopPropagation()
    var files = this.files || e.dataTransfer.files;
    uploadImg(files[0],Editor);
    })
}

function uploadImg(file,Editor){
   var formData = new FormData();
   var fileName=new Date().getTime()+"."+file.name.split(".").pop();
   formData.append('editormd-image-file', file, fileName);

   $.ajax({
       url: Editor.settings.imageUploadURL,
       type: 'post',
       data: formData,
       processData: false,
       contentType: false,
       dataType: 'json',
       success: function (msg) {
           var success=msg['success'];
           if(success==1){
               var url=msg["url"];
               if(/\.(png|jpg|jpeg|gif|bmp|ico)$/.test(url)){
                   Editor.insertValue("![]("+msg["url"]+")");//
               }else{
                   //Editor.insertValue("[]("+msg["url"]+")");//下载文件
               }     
           }else{
               //console.log(msg);
               alert("上传失败");
           }
       }
   });
   
}

在showEditor.html页面引入后,在editormd加载载的回调函数中进行调用即可

<script src="js/uploadImage.js"></script>

initPasteDragImg(this);代码在onload回调中进行调用

testEditor = editormd("test-editormd", {
   width   : "100%",
   height  : 640,
   syncScrolling : "single",
   path    : contextPath + "/lib/",
   imageUpload: true, //同意图片上传
   imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
   imageUploadURL: contextPath +"/article/uploadImg",       //图片上传URL 即后台的图片上传接口URL
   onload : function() {
       initPasteDragImg(this); //支持复制图片直接粘贴
   }
});

效果如图:
在这里插入图片描述

四、demo源码下载

github:   https://github.com/jxlhljh/springbootmarkdowndemotest.git
gitee:  https://gitee.com/jxlhljh/springbootmarkdowndemotest.git
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要用Java开发一个Markdown编辑器,可以使用JavaFX图形化界面库来实现。而要集成Editor.md,可以在JavaFX中嵌入WebView组件,然后加载Editor.md的HTML页面即可。 以下是实现步骤: 1. 在JavaFX中创建一个WebView组件 ``` WebView webView = new WebView(); ``` 2. 通过WebView组件加载Editor.md的HTML页面 ``` String htmlContent = "<html><head><link rel=\"stylesheet\" href=\"https://pandao.github.io/editor.md/css/editormd.min.css\"/></head><body><textarea id=\"editor\"></textarea><script src=\"https://pandao.github.io/editor.md/lib/marked.min.js\"></script><script src=\"https://pandao.github.io/editor.md/lib/prettify.min.js\"></script><script src=\"https://pandao.github.io/editor.md/lib/raphael.min.js\"></script><script src=\"https://pandao.github.io/editor.md/lib/underscore.min.js\"></script><script src=\"https://pandao.github.io/editor.md/lib/sequence-diagram.min.js\"></script><script src=\"https://pandao.github.io/editor.md/lib/flowchart.min.js\"></script><script src=\"https://pandao.github.io/editor.md/lib/jquery.min.js\"></script><script src=\"https://pandao.github.io/editor.md/editormd.min.js\"></script><script type=\"text/javascript\">var testEditor = editormd(\"editor\", {});</script></body></html>"; webView.getEngine().loadContent(htmlContent); ``` 3. 将WebView组件添加到JavaFX的场景中 ``` Scene scene = new Scene(webView); stage.setScene(scene); stage.show(); ``` 这样就可以实现一个简单的Markdown编辑器,并集成Editor.md的功能。当然,还需要实现一些逻辑来获取编辑器中的内容并进行解析和保存等操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值