機能確認と基本設定
富テキストエディタの動作確認後、要件を満たせば実装を進める。
リソースフォルダ:https://pan.baidu.com/s/16hJGPNOso6My7XwivZn_Zw 提取码:a7lt
プロジェクト構成と依存関係
pom.xml設定
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
ディレクトリ構造
config、controller、entity、mapper、serviceディレクトリを生成
設定ファイル
application.yml
spring:
profiles:
active: dev
application-dev.yml
server:
port: 8080
spring:
datasource:
username: root
password: root
url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
mapper-locations: classpath:mapping/*Mapping.xml
type-aliases-package: com.example.blog.entity
logging:
level:
com.example.blog.mapper: DEBUG
静的リソース設定
ResourceConfigクラスで静的リソースを設定
@Component
public class ResourceConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/assets/**").addResourceLocations("classpath:/static/");
registry.addResourceHandler("/editor-resources/**").addResourceLocations("classpath:/editormd/");
registry.addResourceHandler("/uploads/**").addResourceLocations("file:" + System.getProperty("user.dir") + "/uploads/");
}
}
エンティティクラス
public class BlogEntry implements Serializable {
private Integer id;
private String writer;
private String title;
private String content;
// ゲッター・セッター省略
}
コントローラーとビュー
BlogControllerでエディタ表示エンドポイントを定義
@Controller
@RequestMapping("/blog")
public class BlogController {
@GetMapping("/")
public String showEditor() {
return "editor";
}
}
editor.htmlの実装例
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Editor.mdサンプル</title>
<link th:href="@{/editor-resources/css/editormd.css}" rel="stylesheet">
</head>
<body>
<div id="editor-container">
<textarea name="content" style="display:none;"></textarea>
</div>
<script src="/editor-resources/jquery-3.3.1.min.js"></script>
<script src="/editor-resources/editormd.js"></script>
<script>
editormd("editor-container", {
width: "100%",
height: 500,
path: "/editor-resources/lib/",
imageUploadURL: "/blog/upload",
toolbarIcons: ["undo", "redo", "bold", "italic", "image", "code", "preview"],
toolbarHandlers: {
publish: function() {
document.forms[0].submit();
}
}
});
</script>
</body>
</html>
画像アップロード処理
@PostMapping("/upload")
@ResponseBody
public Map<String, Object> uploadImage(@RequestParam("editormd-image-file") MultipartFile file) throws IOException {
String uploadDir = System.getProperty("user.dir") + "/uploads/" + LocalDate.now().getMonthValue() + "月";
Files.createDirectories(Paths.get(uploadDir));
String fileName = "img-" + UUID.randomUUID().toString() + ".jpg";
Path targetPath = Paths.get(uploadDir, fileName);
file.transferTo(targetPath);
Map<String, Object> response = new HashMap<>();
response.put("url", "/uploads/" + LocalDate.now().getMonthValue() + "月/" + fileName);
response.put("success", 1);
return response;
}
データベース操作
BlogMapperインターフェースとXML
@Mapper
public interface BlogMapper {
int insertBlog(BlogEntry blog);
}
<?xml version="1.0" encoding="UTF-8"?>
<mapper namespace="com.example.blog.mapper.BlogMapper">
<insert id="insertBlog" useGeneratedKeys="true" keyProperty="id">
INSERT INTO blog_posts (writer, title, content)
VALUES (#{writer}, #{title}, #{content})
</insert>
</mapper>
データ表示処理
success.htmlでのMarkdownレンダリング
<div id="preview-area">
<textarea style="display:none;">[[${blog.content}]]</textarea>
</div>
<script>
editormd.markdownToHTML("preview-area", {
emoji: true,
flowChart: true,
sequenceDiagram: true
});
</script>