0


vue+springboot读取git的markdown文件并展示

前言

最近,在研究一个如何将我们git项目的MARKDOWN文档获取到,并且可以展示到界面通过检索查到,于是经过几天的摸索,成功的研究了出来

本次前端vue使用的是

Markdown-it 

Markdown-it 是一个用于解析和渲染 Markdown 标记语言的 JavaScript 库。
它采用模块化的设计,提供了灵活的配置选项和丰富的插件系统,使开发者可以根据自己的需要定制 Markdown 的解析和渲染过程。

使用 Markdown-it,你可以将 Markdown 文本解析为 HTML 输出,并且可以根据需要添加功能、扩展语法或修改解析行为

后端springboot使用

JGit

JGit 是一个开源的 Java 实现的 Git 客户端库,它允许开发者在 Java 程序中直接操作 Git 仓库。

JGit 提供了一些核心的 API,使开发者可以使用 Java 代码来访问和操作 Git 仓库,例如创建仓库、提交变更、分支管理、标签管理、克隆远程仓库等。它提供了对 Git 分布式版本控制系统的完整支持,能够满足日常的代码版本管理需求。

但是我们这里单纯只是将其获取git的文件进行展示markdown,因此并用不上

准备工作

前端

在前端,
我使用了

element-ui

前端框架写页面
使用

Markdown-it 

进行解析markdown
使用

axios

连接了前后端
因此,需要安装如上依赖,指令如下:

npm i element-ui
npm i markdown-it
npm i axios

后端

因后端为springboot项目,需要安装springboot的依赖,这里不多赘述,主要是需要安装

JGit

的依赖

<dependency><groupId>org.eclipse.jgit</groupId><artifactId>org.eclipse.jgit</artifactId><version>5.9.0.202009080501-r</version></dependency>

效果演示

那么,在说如何做之前,先介绍一下我做出来的效果吧

首先,我建立了一个

git仓库

,专门用于获取到markdown文件

在这里插入图片描述

为了程序能够获取到文件,我在data文件夹下放了四个markdown文件,并且在程序指定只获取data下的markdown文件

  • 无数据时,前端界面显示如下在这里插入图片描述

当什么关键字都不输入,检索全部markdown文件

在这里插入图片描述
此时展示文件

此时随便点击列表的一个文件查看

在这里插入图片描述

切换另一个
在这里插入图片描述
在这里插入图片描述

以上,我们能够发现,它能够把我们的markdown的

表格

,

图片

以及

表情

正确的显示出来,并且样式排版也过得去,当然,这个是可以自己调整的

多提一句,我有做一个简单的

检索关键字的逻辑

,逻辑如下:

  1. 什么关键字不输入的时候,检索指定文件夹下所有markdown
  2. 当输入关键字,检索文件名,如果包含关键字,则把文件路径加入集合列表
  3. 当文件名不包含关键字,判断文件内容是否包含关键字,包含也把对应文件路径加入列表

前端代码逻辑

界面部分

<template><div><el-page-headercontent="MarkDown展示"/><el-inputv-model="searchKey"placeholder="检索问题"style="position: relative;;width: 70%;left: 0%"></el-input><el-button@click="searchProblem"type="primary"plainstyle="margin: 10px;">检索</el-button><el-card><el-table:data="searchData"style="width: 100%;font-size: 20px;max-height: 500px;overflow-y: auto;background-color: white;"><el-table-columntype="index"label="序号"></el-table-column><el-table-columnlabel="列表项"><templateslot-scope="scope"><span>{{ changePathName(scope.row )}}</span></template></el-table-column><el-table-columnlabel="操作"><templateslot-scope="scope"><div><el-buttontype="primary"size="medium"@click="findMarkDown(scope.row)"style="font-size: 24px;">查看</el-button></div></template></el-table-column></el-table></el-card><!-- 展示区 --><el-cardstyle="position: relative;width: 100%;overflow-x: auto;"header="MarkDown处理文档"><divstyle="position: relative;"><el-cardstyle="background-color:rgb(255, 253, 245);padding: 32px;"v-if="htmlContent"><divv-html="htmlContent"class="v-md-header"></div></el-card><el-cardstyle="background-color:rgb(255, 253, 245);padding: 32px;"v-else><divstyle="position: absolute;left:46.5%;text-align: center;line-height: 0px;font-weight: bold;">请检索并查看文档</div></el-card></div></el-card></div></template>

JavaScript逻辑

<script>// 封装的axios调用后端的方法,如需要则按照自己的项目调用修改即可import{searchProblem,findMarkDownBypath}from"../ajax/api"import MarkdownIt from'markdown-it';exportdefault{name:"MarkDown",data(){return{searchKey:"",searchData:[],// 检索到的问题列表markdownText:'',// 加载好图片的Markdown文本markdownRenderer:null,// 解析markdown渲染器定义htmlContent:'',// 解析为html}},mounted(){this.markdownRenderer =newMarkdownIt();},methods:{// 检索文件searchProblem(){searchProblem(this.searchKey).then(res=>{
            console.log("检索数据:",res);this.searchData = res.data.data;// 赋值检索数据,注意这里的res.data.data请根据自己实际回参更改获取参数this.markdownText ="";// 每次检索清空markdown显示文档内容this.htmlContent ="";// 每次检索清空markdown显示文档内容})},// 根据文件路径查找markdown文件findMarkDown(path){
        console.log("path:",path);findMarkDownBypath(path).then(res=>{
            console.log("markdown内容:",res);this.markdownText = res.data.data;this.htmlContent =this.markdownRenderer.render(this.markdownText);
            console.log(this.htmlContent);})},// 处理字符串,回传的参数实际为:data/学生成绩系统前端.md,将字符串进行截取changePathName(str){if(str){var lastIndex = str.lastIndexOf('/');var result = str.substring(lastIndex +1);return result.replace('.md','');}return str;}}}</script>

在以上,后端传递的路径实际为:

["data/README.en.md","data/README.md","data/学生成绩系统前端.md","data/网上购药商城.md"]

因此为了美观和直观展示,我是有做字符处理的,详情参考如何代码

此外,我后端获取到的markdown的内容实际数据为:

# search_markdown_data

#### Description
用于检索markdown的数据来源

#### Software Architecture
Software architecture description

#### Installation

1.  xxxx
2.  xxxx
3.  xxxx

#### Instructions

1.  xxxx
2.  xxxx
3.  xxxx

#### Contribution

1.  Fork the repository
2.  Create Feat_xxx branch
3.  Commit your code
4.  Create Pull Request

#### Gitee Feature

1.  You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
2.  Gitee blog [blog.gitee.com](https://blog.gitee.com)3.  Explore open source project [https://gitee.com/explore](https://gitee.com/explore)4.  The most valuable open source project [GVP](https://gitee.com/gvp)5.  The manual of Gitee [https://gitee.com/help](https://gitee.com/help)6.  The most popular members  [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)

我的代码中,使用

markdown-it

创建渲染器,将如上数据转换为:

<h1>search_markdown_data</h1><h4>Description</h4><p>用于检索markdown的数据来源</p><h4>Software Architecture</h4><p>Software architecture description</p><h4>Installation</h4><ol><li>xxxx</li><li>xxxx</li><li>xxxx</li></ol><h4>Instructions</h4><ol><li>xxxx</li><li>xxxx</li><li>xxxx</li></ol><h4>Contribution</h4><ol><li>Fork the repository</li><li>Create Feat_xxx branch</li><li>Commit your code</li><li>Create Pull Request</li></ol><h4>Gitee Feature</h4><ol><li>You can use Readme_XXX.md to support different languages, such as Readme_en.md, Readme_zh.md</li><li>Gitee blog <a href="https://blog.gitee.com">blog.gitee.com</a></li><li>Explore open source project <a href="https://gitee.com/explore">https://gitee.com/explore</a></li><li>The most valuable open source project <a href="https://gitee.com/gvp">GVP</a></li><li>The manual of Gitee <a href="https://gitee.com/help">https://gitee.com/help</a></li><li>The most popular members  <a href="https://gitee.com/gitee-stars/">https://gitee.com/gitee-stars/</a></li></ol>

实际为html的数据,因此,我们就可以在界面使用vue的

v-html

展示markdown的内容

css样式

以上我们知道它会将数据转为html的数据,因此,就可以使用css样式调整,以下为我的css样式,供参考:

h1{color: #ff0000;}p{font-size: 16px;line-height: 1.5;}.v-md-header{text-align: left !important;}table{border-collapse: collapse;width: 100%;}th, td{border: 1px solid black;padding: 8px;}th{background-color: #f2f2f2;/* 设置表头的背景颜色 */}tr:nth-child(even){background-color: #dddddd;/* 设置偶数行的背景颜色 */}tr:hover{background-color: #f5f5f5;/* 设置鼠标悬停时的背景颜色 */}h1,h2,h3,h4,h5{border-bottom: 1px #d8d6d6 solid;}img{width: 80%;}

后端代码逻辑

先说下我的查询的方法,

JGIT

我尝试了很久,都只能通过

先克隆到本地

,再读取的方式,于是放弃了研究如何使用JGIT在线读取文件的方式,也许后面我可能研究的出来。

同样,为了保证每次都是最新的文档,我采用了判断是否已经克隆下来了,如果克隆了则更新代码,没有克隆则克隆,来保证每次都是最新代码

在正式执行前,我们需要先定义好需要的参数

// 解析markdown图片正则privatestaticfinalStringMARKDOWN_IMAGE_PATTERN="(!\\[[^\\]]*\\])\\(([^\\)]+)\\)";// 需要克隆git到本机的路径privatefinalStringLOCAL_PATH="E:\\git\\markdown";// 需要获取markdown的git链接privatefinalStringGIT_PATH="https://gitee.com/spring-in-huangxian-county/search_markdown_data.git";// 需要获取的git分支privatefinalStringGIT_BRANCH="master";// 需要抓取的Git内指定文件夹的markdownprivatefinalStringMARK_DOWN_PATH="data";// 当前后端项目的位置,该目的是为了能够找到正确的文件路径privatefinalStringPROJECT_PATH="F:\\gitee\\search_markdown_end";

查询

controller层

@GetMapping("/searchProblem")publicResultVO<List<String>>searchMarkdown(@RequestParam("searchKey")String searchKey)throwsException{// 获取Git仓库中的Markdown文档列表try{List<String> markdownFiles =newMarkDownService().getGitDataFilePath();List<String> results =newArrayList<>();if(StringUtils.isEmpty(searchKey)){
                results.addAll(markdownFiles);}else{for(String path:markdownFiles){// 如果标题包含检索关键字加入列表if(path.contains(searchKey)){
                        results.add(path);}else{// 判断具体内容是否包含关键字,是则加入列表if(newMarkDownService().isContainSearchKeyForContent(searchKey,path)){
                            results.add(path);}}}}returnnewResultVO<>(0,"OK",results);}catch(Exception e){returnnewResultVO<>(1,e.getMessage());}}

ResultVO为封装的响应体,如有兴趣可参考我之前文章
MarkDownService为service层文件名

service层

克隆和拉取git的方式读取文件,获取文件路径

// 克隆和拉取git的方式读取文件publicList<String>getGitDataFilePath()throwsException{File localPath =newFile(LOCAL_PATH);String remoteUrl =GIT_PATH;String branchName =GIT_BRANCH;// 或者其他分支名称String folderPath =MARK_DOWN_PATH;// data文件夹的路径List<String> markDownFilePathList =newArrayList<>();Repository repository;if(localPath.exists()){
            repository =openLocalRepository(localPath);pullLatestChanges(repository);}else{
            repository =cloneRepository(localPath, remoteUrl);}try(Git git =newGit(repository)){Iterable<RevCommit> commits = git.log().add(repository.resolve(branchName)).call();RevCommit commit = commits.iterator().next();try(RevWalk revWalk =newRevWalk(repository)){RevTree tree = revWalk.parseTree(commit.getTree());try(TreeWalk treeWalk =newTreeWalk(repository)){
                    treeWalk.addTree(tree);
                    treeWalk.setRecursive(true);while(treeWalk.next()){if(treeWalk.getPathString().startsWith(folderPath)&& treeWalk.getPathString().endsWith(".md")){System.out.println("Found markdown file: "+ treeWalk.getPathString());// 这里可以根据需要进行具体的处理,比如读取文件内容等
                            markDownFilePathList.add(treeWalk.getPathString());}}}}}catch(IOException|GitAPIException e){
            e.printStackTrace();}return markDownFilePathList;}

打开本地git

// 打开本地git项目privateRepositoryopenLocalRepository(File localPath)throwsIOException{System.out.println("Opening existing repository...");Git git =Git.open(localPath);return git.getRepository();}

克隆代码

// 克隆gitprivateRepositorycloneRepository(File localPath,String remoteUrl)throwsGitAPIException{System.out.println("Cloning repository...");Git git =Git.cloneRepository().setURI(remoteUrl).setDirectory(localPath).call();return git.getRepository();}

拉取最新代码

//拉取git最新代码privatevoidpullLatestChanges(Repository repository)throwsGitAPIException{System.out.println("Pulling latest changes...");Git git =newGit(repository);PullCommand pull = git.pull().setTimeout(30);
        pull.call();}

检查文件内容是否包含关键字

/**
     * @param searchKey 检索关键字
     * @param path markdown文本路径
     * @desc 通过关键字和路径找到指定markdown文件是否内容包含关键字
     * */publicBooleanisContainSearchKeyForContent(String searchKey,String path){Boolean containFlag =false;String content ="";try{
            content =findMarkDownBypathNoWithImage(path);}catch(Exception e){System.out.println("获取markdown文本失败:"+e.getMessage());}if(content.contains(searchKey)){
            containFlag =true;}return containFlag;}

要判断文件内容是否包含关键字,不需要将文件图片进行解析,直接获取文件内容

publicStringfindMarkDownBypathNoWithImage(String filePath)throwsException{String localPath =LOCAL_PATH;String markDownContent ="";if(filePath.endsWith(".md")){File markdownFile =newFile(localPath, filePath);try(Scanner scanner =newScanner(markdownFile)){StringBuilder contentBuilder =newStringBuilder();while(scanner.hasNextLine()){
                    contentBuilder.append(scanner.nextLine()).append("\n");}
                markDownContent = contentBuilder.toString();System.out.println("Markdown file content:\n"+ markDownContent);}catch(IOException e){thrownewException(e.getMessage());}}return markDownContent;}

根据路径获取文件

以下为会解析图片的方式进行获取文件

controller层

@GetMapping("findMarkDownBypath")publicResultVO<String>findMarkDownBypath(@RequestParam("path")String path)throwsException{try{returnnewResultVO<>(newMarkDownService().findMarkDownBypathWithImage(path));}catch(Exception e){returnnewResultVO<>(1,e.getMessage());}}

service层

publicStringfindMarkDownBypathWithImage(String filePath)throwsException{String localPath =LOCAL_PATH;String markDownContent ="";if(filePath.endsWith(".md")){File markdownFile =newFile(localPath, filePath);try(Scanner scanner =newScanner(markdownFile)){StringBuilder contentBuilder =newStringBuilder();while(scanner.hasNextLine()){
                    contentBuilder.append(scanner.nextLine()).append("\n");}String markdownContent = contentBuilder.toString();
                markDownContent =loadImages(markdownContent,filePath);// 在这里得到了具体的markdown文件内容System.out.println("Markdown file content:\n"+ markdownContent);}catch(IOException e){thrownewException(e.getMessage());}}return markDownContent;}

解析图片

publicStringloadImages(String markdownContent,String markdownFilePath){Pattern pattern =Pattern.compile(MARKDOWN_IMAGE_PATTERN);Matcher matcher = pattern.matcher(markdownContent);String localPath =LOCAL_PATH;StringBuffer sb =newStringBuffer();while(matcher.find()){String originalImageTag = matcher.group(0);String altText = matcher.group(1);String imagePath = matcher.group(2);try{String absoluteImagePath =getAbsoluteImagePath(imagePath, markdownFilePath);
                absoluteImagePath = absoluteImagePath.replace(PROJECT_PATH,localPath);String imageData =loadImage(absoluteImagePath);String transformedImageTag ="![Image]("+ imageData +")";
                matcher.appendReplacement(sb, transformedImageTag);}catch(IOException e){// 图像加载出错,可以根据实际需求进行处理
                e.printStackTrace();}}
        matcher.appendTail(sb);return sb.toString();}
publicstaticStringloadImage(String imagePath)throwsIOException{File imageFile =newFile(imagePath);// 读取图像文件的字节数组byte[] imageData =FileUtils.readFileToByteArray(imageFile);// 将字节数组转换为Base64编码字符串String base64ImageData =java.util.Base64.getEncoder().encodeToString(imageData);return"data:image/png;base64,"+ base64ImageData;}
publicstaticStringgetAbsoluteImagePath(String imagePath,String markdownFilePath){File markdownFile =newFile(markdownFilePath);String markdownDirectory = markdownFile.getParent();String absoluteImagePath =newFile(markdownDirectory, imagePath).getAbsolutePath();return absoluteImagePath;}

依赖

为了防止出现依赖可能缺失的情况,可参考我的项目的maven

<?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.2.RELEASE</version><relativePath/><!-- lookup parent from repository --></parent><modelVersion>4.0.0</modelVersion><groupId>org.hxc.common</groupId><artifactId>CommonBack</artifactId><version>1.0</version><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target><mysql.version>5.1.47</mysql.version><druid.version>1.1.16</druid.version><log4j2.version>2.17.0</log4j2.version><mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.2.2.RELEASE</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.16.18</version><optional>true</optional></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.3.8</version></dependency><!--        swagger--><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.9.2</version><exclusions><exclusion><groupId>io.swagger</groupId><artifactId>swagger-annotations</artifactId></exclusion><exclusion><groupId>io.swagger</groupId><artifactId>swagger-models</artifactId></exclusion></exclusions></dependency><dependency><groupId>io.swagger</groupId><artifactId>swagger-annotations</artifactId><version>1.5.21</version></dependency><dependency><groupId>io.swagger</groupId><artifactId>swagger-models</artifactId><version>1.5.21</version></dependency><!--        swagger的ui--><dependency><groupId>com.github.xiaoymin</groupId><artifactId>swagger-bootstrap-ui</artifactId><version>1.9.6</version></dependency><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpmime</artifactId><version>4.5</version></dependency><dependency><groupId>commons-codec</groupId><artifactId>commons-codec</artifactId><version>1.9</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId></dependency><dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.8.5</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.76</version></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>${mybatis.spring.boot.version}</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>${druid.version}</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>${mysql.version}</version></dependency><!-- pagehelper --><dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>1.2.5</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.15.0</version></dependency><!--    文件处理--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.6</version></dependency><!--   POI excel处理依赖     --><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>3.9</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>3.9</version></dependency><!--   工具类     --><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.3.8</version></dependency><!--        <dependency>--><!--            <groupId>org.eclipse.jgit</groupId>--><!--            <artifactId>org.eclipse.jgit</artifactId>--><!--            <version>4.4.1.201607150455-r</version>--><!--        </dependency>--><dependency><groupId>org.eclipse.jgit</groupId><artifactId>org.eclipse.jgit</artifactId><version>5.9.0.202009080501-r</version></dependency></dependencies><build><finalName>common_end</finalName><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><version>3.1.0</version><configuration><archive><manifest><addClasspath>true</addClasspath><mainClass>com.hxc.common.MarkDownApplication</mainClass><classpathPrefix>libs/</classpathPrefix></manifest></archive></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-dependency-plugin</artifactId><version>3.1.0</version><executions><execution><id>copy-dependencies</id><phase>package</phase><goals><goal>copy-dependencies</goal></goals><configuration><outputDirectory>${project.build.directory}/libs</outputDirectory></configuration></execution></executions></plugin><!--跳过junit--><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><configuration><skip>true</skip></configuration></plugin></plugins></build></project>

git

以下为我实现读取git的markdown的项目,可供参考

前端

后端

结语

以上为我实现vue+springboot读取git的markdown文件并展示的过程

标签: vue.js spring boot git

本文转载自: https://blog.csdn.net/xc9711/article/details/134563452
版权归原作者 相与还 所有, 如有侵权,请联系我们删除。

“vue+springboot读取git的markdown文件并展示”的评论:

还没有评论