1.前言
富文本编辑器的整合是一件十分简单的事情,在遇到困难时,不妨看看editor文件夹中的官方演示文件,可以让我们知道某些过程是如何实现的以及通过最简练的代码完成前后端的设计 (在文件的examples文件夹中有着许多测试页面,可以参照着这些页面完成富文本框的搭建)
2.下载Editor源代码
3.搭建SpringBoot工程,导入依赖
这里比较核心的依赖是fastjson 与数据库交互的一些依赖就自行导入了,这次的工程是分布式工程,clint层和sever层是分离的,这里仅展示clint部分的依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--dubbo场景启动器-->
<dependency>
<groupId>io.dubbo.springboot</groupId>
<artifactId>spring-boot-starter-dubbo</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.example</groupId>
<artifactId>dubbo_boke</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--模版引擎-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
</dependencies>
4.数据库设计
重点就是用longtext类型来接收数据
5.配置文件application.properties
这里的重点就是规定静态文件的位置(其实这个配置感觉不太用得到)
#修改端口号
server.port=8080
#配置视图的位置
spring.freemarker.template-loader-path=classpath:/templates/
#配置视图的后缀
spring.freemarker.suffix=.ftl
#dubbo相关配置
spring.dubbo.application.name=client-student
#2181 zookeeper默认端口号
spring.dubbo.registry.address=zookeeper://192.168.163.129:2181
spring.dubbo.scan=com.example.demo.controller
##规定静态文件的位置************
#spring.mvc.static-path-pattern=/static/**
spring.servlet.multipart.location=D:/项目文件夹/boke_clint/boke_clint/src/main/resources/static
spring.web.resources.static-locations=classpath:static/, file:${spring.servlet.multipart.location}
6.前端页面和需要用到的资源文件
要想在前端成功运行富文本编辑器,资源文件是必不可少的,这里我遇到了第一个问题,就是导入资源文件失败,在使用<script th:src="@{/js/editormd.min.js}"></script>标签时,要么干脆获取不到文件,要么获取不全,这里由于某些原因(可能是默认配置,可能是某些配置文件和依赖导致),我的资源默认文件夹变成了webapps文件夹,例如“/picture/01.jpg” 表示webapps文件夹下的picture文件夹下的01.jpg图片,在映入资源时这样写,虽然idea都找不到文件位置,但确实是可以获取到位置的
<script th:src="@{/js/editormd.min.js}"></script> 获取不到(我也不知道啥原因)
<script src="/examples/js/jquery.min.js"></script> 这样就可以获取到了
关于资源的引入,这里不管在官方的演示文件还是他人的博客中,引入js文件的代码都只有几行,但事实是只引入这些文件是不够的(这里的意思是,代码上只需要引入这些文件,但是事实上服务器会获取到很多关联到的资源,所以资源文件夹的名称,位置是很重要的,)
为了解决以上问题,我的引入方法是将editor中的所有文件和文件夹放入web-apps文件夹中(在浏览器调用成功后可以看到调用到的资源文件,可以将多余文件删除)
前端网页运行后,在检查里面可以查看到使用到的资源文件,除了这些文件之外的文件可以删除
6.1前端页面
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8" />
<title>Simple example - Editor.md examples</title>
<link rel="stylesheet" href="/examples/css/style.css" />
<link rel="stylesheet" href="/css/editormd.css" />
<link rel="shortcut icon" href="https://pandao.github.io/editor.md/favicon.ico" type="image/x-icon" />
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css"
integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
</head>
<body>
<div id="layout">
<header>
<h1 align="center" style="font-size: xx-large">博客创作中心</h1>
</header>
<form name="mdEditorForm">
<div style="width: 30%;margin-left: 30%" align="center"><input class="form-control" placeholder="标题" type="text" name="bokeTitle"><br></div>
<div id="test-editormd">
<textarea style="display:none;" name="bokeText"></textarea>
</div>
</form>
</div>
<script src="/examples/js/jquery.min.js"></script>
<script src="/editormd.min.js"></script>
<script type="text/javascript">
var testEditor;
$(function() {
testEditor = editormd("test-editormd", {
width : "90%",
height : 640,
syncScrolling : "single",
path : "../lib/",
// 表示支持上传图片
imageUpload : true,
imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
// 上传图片的请求接口
imageUploadURL : "/boke/imageUpload",
// 工具栏图标的设置,大家可以自定义。比如 publish就是我定义的。
toolbarIcons : function () {
return ["undo","redo","|","bold","del","italic","quote","ucwords","uppercase","lowercase","|","h1","h2","h3","h4","h5","h6","|","list-ul","list-ol","hr","|","link","reference-link","image","code","preformatted-text","code-block","table","datetime","emoji","html-entities","pagebreak","|","goto-line","watch","preview","fullscreen","clear","search","|","help","info", "||", "publish"];
},
// 自定义图标后,定义图标对应的文字
toolbarIconTexts: {
publish: "<span bgcolor='gray'>发布</span>"
},
// 自定义图标的触发
toolbarHandlers : {
publish: function (cm, icon, cursor, selection) {
mdEditorForm.method = "post";
mdEditorForm.action = "/boke/publish";//提交至服务器的路径
mdEditorForm.submit();
}
}
});
/*
// or
testEditor = editormd({
id : "test-editormd",
width : "90%",
height : 640,
path : "../lib/"
});
*/
});
</script>
</body>
</html>
前端点击发布后,数据会传输回后端,后端有两个方法,一个是取到前端的数据存入数据库,第二个是图片的处理,这里先给出处理图片的工具类
package com.example.demo.utils;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
/**
* 文件上传工具类
*/
public class FileUtils {
// static目录下的upload目录可自己建,也可不建。因为在上传的时候,会判断是否存在,若不存在便自动创建
private static final String prePath = System.getProperty("user.dir") + "/src/main/resources/static/upload/";
/**
* 上传文件
* @param file
* @return 返回文件路径(以相对路径放回)
*/
public static String uploadFile(MultipartFile file) {
if(file.isEmpty()) {
return "";
}
// 获取原文件名
String originFileName = file.getOriginalFilename();
// 我们通过UUID 来重新重组文件名
String uid = UUID.randomUUID().toString();
assert originFileName != null;
String suffix = originFileName.substring(originFileName.lastIndexOf('.') + 1);
String path = prePath + uid + "." + suffix;
String returnPath = "/upload/" + uid + "." + suffix;
File newFile = new File(path);
if(newFile.getParentFile() != null && !newFile.getParentFile().exists()) {
System.out.println("创建目录ing");
// 上面的 newFile.getParentFile() 已经保证了不为null.
if(newFile.getParentFile().mkdirs()) {
System.out.println("创建目录成功");
}else {
System.out.println("创建目录失败");
return "";
}
}
try {
file.transferTo(newFile);
} catch (IOException e) {
e.printStackTrace();
return "";
}
return returnPath;
}
}
这里是加载图片的方法,就是前端点击传输图片后,会调用到这个方法
@RequestMapping("/imageUpload")
@ResponseBody
// 注意RequestParam中的name,不可改。
public JSONObject imageUpload(@RequestParam(value = "editormd-image-file", required = true) MultipartFile image) {
JSONObject jsonObject = new JSONObject();
if (image != null) {
String path = FileUtils.uploadFile(image);
System.out.println(path);
jsonObject.put("url", path);
jsonObject.put("success", 1);
jsonObject.put("message", "upload success!");
System.out.println(jsonObject);
System.out.println("返回了json串,不知道前端能不能成功解析");
return jsonObject;
}
jsonObject.put("success", 0);
jsonObject.put("message", "upload error!");
return jsonObject;
}
这个方法是前端发表文章后后端的接受方法
@RequestMapping("/publish")
public String publishArticle(Boke boke, HttpSession session, HttpServletResponse response) {
System.out.println(boke);
User user = (User) session.getAttribute("userInfo");
if (user == null) return "bokeHomeYoke";
//这里需要将用户的名称等信息填入,由于还在测试阶段,就先空着,只把博客信息注入数据库
boke.setBokeUserId(user.getUserId());
boke.setBokeUserNichen(user.getUserNichen());
boke.setBokeHot(0);
boke.setBokeGoodNum(0);
boke.setBokeLookNum(0);
Integer i = bokeService.addBoke(boke);
this.bokeHome(session);
return "bokeHome";
}
这里结束,你就可以写文章,并且完成图片的插入,下面就是要获取到你之前写的文章并且展示在前端界面上了
//展示文章的前端页面
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>文章</title>
<link rel="stylesheet" href="/examples/css/style.css"/>
<link rel="stylesheet" href="/css/editormd.css"/>
<link rel="shortcut icon" href="https://pandao.github.io/editor.md/favicon.ico" type="image/x-icon"/>
</head>
<body class="back">
<div id="layout">
<div style="font-size: xx-large;font-family: 微软雅黑;margin-left: 40%">
${bokeDetail.bokeTitle}
</div>
<div style="font-size: large;font-family: 微软雅黑;margin-left: 40%">
作者:${bokeDetail.bokeUserNichen}
</div>
<div id="test-editormd" style="width: 60%;margin-left: 20%;margin-right: 10%">
<textarea class="back" style="display:none;" placeholder="markdown">${bokeDetail.bokeText}</textarea>
</div>
</div>
<link rel="stylesheet" href="/css/editormd.css"/>
<script src="/examples/js/jquery.min.js"></script>
<script src="/lib/marked.min.js"></script>
<script src="/lib/prettify.min.js"></script>
<script src="/lib/raphael.min.js"></script>
<script src="/lib/underscore.min.js"></script>
<script src="/lib/sequence-diagram.min.js"></script>
<script src="/lib/flowchart.min.js"></script>
<script src="/lib/jquery.flowchart.min.js"></script>
<script src="/editormd.js"></script>
<script type="text/javascript">
var testEditor;
$(function () {
testEditor = editormd.markdownToHTML("test-editormd", {
width: "50%",
height: 700,
path: "./lib/",
preview: true,
watch: true,
editor: false,
})
})
</script>
</body>
</html>
到此,Editor整合完毕,其中有许多细节之处还没有讲清,只能说多摸索了之后就知道如何去整合
如果在整合时代码报错或者前端页面加载失败或者获取不到数据,查看官方提供的例子是最好的方式