Vue - 后端解析md文件并在前端显示 (代码高亮+md中图片显示)

16 篇文章 0 订阅
14 篇文章 0 订阅

之前在做的个人博客,前端直接引入markdown文件并显示,当时的做法如下:
在这里插入图片描述
这里如果用import()动态引入,必须写入静态路径,十分麻烦,难道项目部署上线时我还需要到这里来修改这个静态路径吗?肯定不会,所以我思来想去,还是采用后端解析md文件前端显示的流程,这样一系列文件路径的配置可以放在后端。

1. 后端解析md文件

1.1 Springboot中配置md文件存储路径以及相关映射

server:
  #端口
  port: 4000
  #本地路径  为了映射md中的图片文件,必须填
  address: localhost


#文件上传路径
file:
  upload:
    #注意md文件存放位置,注意最后结尾是'/'
    abpath: F:/note/
    urlpath: /note/**

    #存放图像的路径映射,与md文件中的/assets对应
    mdImageDir: /assets/**

在这里插入图片描述

1.2 根据配置建立url映射

这里需要在springboot中定义MyWebAppConfig进行映射配置:

@Configuration
public class MyWebAppConfig implements WebMvcConfigurer {

    //文件夹绝对路径
    @Value("${file.upload.abpath}")
    private String abpath;

    //文件夹url
    @Value("${file.upload.urlpath}")
    private String ulrpath;

    //图片url
    @Value("${file.upload.mdImageDir}")
    private String mdImageDir;

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
    	//md文件位置建立映射
        registry.addResourceHandler(ulrpath).addResourceLocations("file:"+abpath);
		//图片位置建立映射
        registry.addResourceHandler(mdImageDir).addResourceLocations("file:"+abpath+"assets/");
        registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");

    }
}

1.3 Controller层

    @Value("${file.upload.abpath}")
    private String abpath;

    //文件夹url
    @Value("${file.upload.mdImageDir}")
    private String mdImageDir;

    //端口
    @Value("${server.port}")
    private String port;

    //地址
    @Value("${server.address}")
    private String address;


    @Autowired
    private ArticleService articleService;



  //根据id返回md的str
    @GetMapping("/api/getStrById/{id}")
    public String getStrById(@PathVariable("id") long id) throws FileNotFoundException {

        //通过id从数据库获取article对象
        Article article = articleService.queryById(id);

        //如果article不存在则抛出异常
        if(StringUtils.isEmpty(article)){
            throw new FileNotFoundException("文件不存在");
        }

        //组装成路径,比如F:\note\123.txt
        String path = abpath + article.getName();

        //通过路径获取str
        String str = null;
        try {
        	//自定义md2Html函数转化md文件为html
            str = FileUtil.md2Html(path,"http://"+address+":"+port+"/assets/");
        } catch (IOException e) {
            e.printStackTrace();
            //抛出自已定义的异常
            throw new FileNotFoundException("文件所在路径找不到文件");
        }
        return str;
    }

articleService的定义就不再赘述,之前博客说过,这里不是重点。FileNotFoundException的定义和拦截,见我之前的博客,这里也不说了。这里重点是md2Html()函数,即将md文件转化为string,我定义在FileUtil类中,这里用到了PegDownProcessor这个类,需要首先导入依赖:

 <!--md转html工具-->
   <dependency>
       <groupId>org.pegdown</groupId>
       <artifactId>pegdown</artifactId>
       <version>1.6.0</version>
   </dependency>

md2Html()函数定义:

   public static String md2Html(String path,@Nullable String imgaddr) throws IOException {
        String html = null;
        FileReader r = new FileReader(path);
        char[] cbuf = new char[1024];
        while( r.read(cbuf) != -1){
            html += new String(cbuf);
        }

        PegDownProcessor pdp = new PegDownProcessor(Integer.MAX_VALUE);
        html = pdp.markdownToHtml(html);

        if(!StringUtils.isEmpty(imgaddr)){
            //将html中路径"assets/***" 变为 "http://localhost:4000/assets/"
            String newHtml = StringUtils.replace(html, "assets/", imgaddr);
            return newHtml;
        }
        return html;
    }

由于md文件保存图片默认路径都是"assets/"之下(这里需要在Typroa中设置),如图:
在这里插入图片描述
这样在前端是无法访问到的,所以在md2Html()函数中对"assets/"路径进行替换:

 //将html中路径"assets/***" 变为 "http://localhost:4000/assets/"
 String newHtml = StringUtils.replace(html, "assets/", imgaddr);

访问测试看看:
在这里插入图片描述

2. 前端显示html

我前端是用vue写的,相关流程可以见我之前的博客。

2.1 axios定义交互函数

//根据地址获取md的html
export function getStrById(id){
  return axios({
    method: 'get',
    url: `/getStrById/${id}`,
   
  });
}

2.2 代码高亮配置

这里主要参考了https://blog.csdn.net/qq_39826207/article/details/109265099里的做法。

  1. 安装依赖
// 用于代码高亮显示
cnpm install highlight.js -S
 
// 代码行号显示插件
cnpm install highlightjs-line-numbers.js

  1. 在main.js文件中引入highlight.js
//引入代码高亮
import highlight from 'highlight.js';
Vue.use(highlight);
  1. 创建一个全局指令,用于代码高亮显示
    在main.js文件中,创建一个自定义全局指令,如下所示:
//将代码高亮封装成一个指令
Vue.directive('highlight', (el) => {
  let blocks = el.querySelectorAll('pre code')
  blocks.forEach((block) => {
    highlight.highlightBlock(block)

  })

})
  1. 在具体Article.vue中进行显示
<template>
  <div>
     <!--这里只需要加上v-highlight即可高亮-->
     <div v-highlight v-html="msg" class="styleClass"></div>
  </div>
</template>
<script>
import axios from '@/request'
import {getStrById} from '@/api/article'
// 引入高亮样式,其他样式见https://blog.csdn.net/qq_39826207/article/details/109265099
import "highlight.js/styles/atelier-forest-dark.css";
export default {
  data(){
    return{
      arId: '',
      msg:'',
      key: 0,
      arName:''
      
    }
  },
  methods:{
     //获取md文件并解析为html
      async getMd(){
        // 通过id获取md文件的str
        //this.$route.params.id是指$route传过来的id,即后台数据库id
        await getStrById(this.$route.params.id).then(res=>{
          console.log(res)
          this.msg=  res;
        })

        //强制重新渲染
        this.key += 1     
     },

  },
  mounted(){
    this.getMd();
  }
}
</script>

最终测试效果如图:
在这里插入图片描述

3. 小结

关于代码高亮显示后的行号显示,一直没有搞定!还在继续寻找解决方案,后续会放上来。希望能够对正在用vue+springboot开发项目的朋友有所帮助,有问题请一起讨论,大家共同进步。

  • 7
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值