基于.NetCore开发博客项目 StarBlog - (4) markdown博客批量导入

系列文章

  • 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客?

  • 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目

  • 基于.NetCore开发博客项目 StarBlog - (3) 模型设计

  • 基于.NetCore开发博客项目 StarBlog - (4) markdown博客批量导入

  • ...

前言

上周介绍了博客的模型设计,现在模型设计好了,要开始导入数据了。

我们要把一个文件夹内的所有markdown文件导入,目录结构作为文章的分类,文件名作为文章的标题,同时把文件的创建、更新日期作为文章的发表时间。

大概的思路就是先用.Net的标准库遍历目录,用第三方的markdown解析库处理文章内容,然后通过ORM写入数据库。

PS:明天就是五一劳动节了,祝各位无产阶级劳动者节日快乐~

相关技术

  • 文件IO相关API

  • 正则表达式

  • ORM:FreeSQL

  • markdown解析库:Markdig

开始写代码

我们首先从最关键的markdown内容解析、图片提取、标题处理说起。

为了处理markdown内容,我搜了一下相关资料,发现.Net Core目前能用的只有Markdig这个库,由于还处在开发阶段,没有完整文档,只能边看github主页的一点点说明边自己结合例子来用。没办法,没别的好的选择,又懒得(菜)造轮子,只能将就了。

Markdig官网地址:https://github.com/xoofx/markdig

StarBlog.Migrate项目里新建一个Class:PostProcessor,我们要在这个class里实现markdown文件相关的处理逻辑。

PostProcessor.cs的完整代码在这:https://github.com/Deali-Axy/StarBlog/blob/master/StarBlog.Migrate/PostProcessor.cs

构造方法:

private readonly Post _post;
private readonly string _importPath;
private readonly string _assetsPath;public PostProcessor(string importPath, string assetsPath, Post post) {_post = post;_assetsPath = assetsPath;_importPath = importPath;
}

其中

  • Post:我们上一篇里设计的文章模型

  • importPath:要导入的markdown文件夹路径

  • assetsPath:资源文件存放路径,用于存放markdown里的图片,本项目设置的路径是StarBlog.Web/wwwroot/media/blog

文章摘要提取

文章摘要提取,我做了简单的处理,把markdown内容渲染成文本,然后截取前n个字形成摘要,代码如下:

public string GetSummary(int length) {return _post.Content == null? string.Empty: Markdown.ToPlainText(_post.Content).Limit(length);
}

文章状态和标题处理

之前在用本地markdown文件写博客的时候,出于个人习惯,我会在文件名里加上代表状态的前缀,例如未完成的文章是这样的:

(未完成)StarBlog博客开发笔记(4):markdown博客批量导入

或者已完成但未发布,会加上(未发布)

等到发布之后,就把前缀去掉,所以在导入的时候,我要用正则表达式对这个前缀进行提取,让导入数据库的博客文章标题不要再带上前缀了。

代码如下

public (string, string) InflateStatusTitle() {const string pattern = @"^((.+))(.+)$";var status = _post.Status ?? "已发布";var title = _post.Title;if (string.IsNullOrEmpty(title)) return (status, $"未命名文章{_post.CreationTime.ToLongDateString()}");var result = Regex.Match(title, pattern);if (!result.Success) return (status, title);status = result.Groups[1].Value;title = result.Groups[2].Value;_post.Status = status;_post.Title = title;if (!new[] { "已发表", "已发布" }.Contains(_post.Status)) {_post.IsPublish = false;}return (status, title);
}

逻辑很简单,判断标题是否为空(对文件名来说这不太可能,不过为了严谨一点还是做了),然后用正则匹配,匹配到了就把状态提取出来,没匹配到就默认"已发布"

图片提取 & 替换

markdown内容处理比较复杂的就是这部分了,所以我之前就把这部分单独拿出来写了一篇文章来介绍,所以本文就不再重复太多,详情可以看我前面的这篇文章:C#解析Markdown文档,实现替换图片链接操作

然后回到我们的博客项目,这部分的代码如下

public string MarkdownParse() {if (_post.Content == null) {return string.Empty;}var document = Markdown.Parse(_post.Content);foreach (var node in document.AsEnumerable()) {if (node is not ParagraphBlock { Inline: { } } paragraphBlock) continue;foreach (var inline in paragraphBlock.Inline) {if (inline is not LinkInline { IsImage: true } linkInline) continue;if (linkInline.Url == null) continue;if (linkInline.Url.StartsWith("http")) continue;// 路径处理var imgPath = Path.Combine(_importPath, _post.Path, linkInline.Url);var imgFilename = Path.GetFileName(linkInline.Url);var destDir = Path.Combine(_assetsPath, _post.Id);if (!Directory.Exists(destDir)) Directory.CreateDirectory(destDir);var destPath = Path.Combine(destDir, imgFilename);if (File.Exists(destPath)) {// 图片重名处理var imgId = GuidUtils.GuidTo16String();imgFilename = $"{Path.GetFileNameWithoutExtension(imgFilename)}-{imgId}.{Path.GetExtension(imgFilename)}";destPath = Path.Combine(destDir, imgFilename);}// 替换图片链接linkInline.Url = imgFilename;// 复制图片File.Copy(imgPath, destPath);Console.WriteLine($"复制 {imgPath} 到 {destPath}");}}using var writer = new StringWriter();var render = new NormalizeRenderer(writer);render.Render(document);return writer.ToString();
}

实现的步骤大概是这样:

  • 用Markdig库的markdown解析功能

  • 把所有图片链接提取出来

  • 然后根据我们前面在构造方法中传入的importPath导入目录,去拼接图片的完整路径

  • 接着把图片复制到assetsPath里面

  • 最后把markdown中的图片地址替换为重新生成的图片文件名

小结

目前这个方案处理大部分markdown中的图片都没问题,但是仍存在一个问题!

图片文件名带空格时无法识别!

这个问题算是Markdig库的一个缺陷?吧,我尝试读了一下Markdig的代码想看看能不能fix一下,很遗憾我没读懂,所以暂时没有很好的办法,只能向官方提个issues了,这个库的更新很勤快,有希望让官方来修复这个问题。

遍历目录

前面说了关键的部分,现在来说一下比较简单的遍历目录文件,对文件IO用得很熟练的同学请跳过这部分~

我用的是递归的方式来实现的,参考微软官方的一篇博客:https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/file-system/how-to-iterate-through-a-directory-tree

关键代码如下,完整代码在这:https://github.com/Deali-Axy/StarBlog/blob/master/StarBlog.Migrate/Program.cs

void WalkDirectoryTree(DirectoryInfo root) {Console.WriteLine($"正在扫描文件夹:{root.FullName}");FileInfo[]? files = null;DirectoryInfo[]? subDirs = null;try {files = root.GetFiles("*.md");}catch (UnauthorizedAccessException e) {Console.WriteLine(e.Message);}catch (DirectoryNotFoundException e) {Console.WriteLine(e.Message);}if (files != null) {foreach (var fi in files) {Console.WriteLine(fi.FullName);// 处理文章的代码,省略}}subDirs = root.GetDirectories();foreach (var dirInfo in subDirs) {if (exclusionDirs.Contains(dirInfo.Name)) {continue;}if (dirInfo.Name.EndsWith(".assets")) {continue;}WalkDirectoryTree(dirInfo);}
}

用的这个方法叫做“前序遍历”,即先处理目录下的文件,然后再处理目录下的子目录。

递归的方法写起来比较简单,但是有一个缺陷是如果目录结构嵌套太多的话,可能会堆栈溢出,可以考虑换用基于Stack<T>模式的遍历,不过作为博客的目录层级结构应该不会太多,所以我只用简单的~

写入数据库

本项目用到的ORM是FreeSQL,ORM操作在后续的网站开发中会有比较多的介绍,因此本文略过,文章数据写入数据库的代码很简单,可以直接看:https://github.com/Deali-Axy/StarBlog/blob/master/StarBlog.Migrate/Program.cs

结束

OK,博客批量导入就介绍了这么多,几个麻烦的地方处理好之后也没啥难度了,有了文章数据之后,才能方便接下来开始开发博客网站~

大概就这些了,下篇文章见~

同时所有项目代码已经上传GitHub,欢迎各位大佬Star/Fork!

  • 博客后端+前台项目地址:https://github.com/Deali-Axy/StarBlog

  • 管理后台前端项目地址:https://github.com/Deali-Axy/StarBlog-Admin

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/288256.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(一)python3 只需3小时带你轻松入门—— 编程尝试

什么是函数&#xff1f; 在编程中&#xff0c;函数和通常数学中的函数概念并不完全相同&#xff1b;编程中的函数更接近于一个写好的工具&#xff0c;在开发某些功能时&#xff0c;所需要到该函数&#xff0c;就把该函数拿过来使用。 输出/显示 运行python程序时显示指定的文本…

HTTP 笔记与总结(7)HTTP 缓存(配合 Apache 服务器)

在网络上&#xff0c;有一些缓存服务器&#xff0c;另外浏览器自身也有缓存功能。 例如&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Document</title> </head> <body&…

linux之dos2unix命令解决vi打开文件行尾巴显示^M

1 问题 有时候我们用vi打开文件,行尾巴显示^M,我之前也不知道这个是为什么,后面才了解到,DOS格式的文本文件在Linux平台,用较低版本的vi打开文件行尾会显示^M,DOS下的文本文件是以\r\n作为断行标志的,表示成十六进制就是0D 0A。而Unix下的文本文件是以\n作为断行标志的,…

bootstrap 一排5个_Bootstrap5 列(Columns)

对其使用flexbox对齐工具来垂直和水平对齐列。垂直对齐One of three columnsOne of three columnsOne of three columnsOne of three columnsOne of three columnsOne of three columnsOne of three columnsOne of three columnsOne of three columnsOne of three columnsOne o…

ASP.NET MVC下的四种验证编程方式

ASP.NET MVC采用Model绑定为目标Action生成了相应的参数列表,但是在真正执行目标Action方法之前,还需要对绑定的参数实施验证以确保其有效性,我们将针对参数的验证成为Model绑定。总地来说,我们可以采用4种不同的编程模式来进行针对绑定参数的验证。 目录 一、手工验证绑…

台式计算机idc数据排名,IDC数据出炉 海尔电脑排名持续上升

2011年上半年&#xff0c;国内PC产品呈现缓慢增长态势。近期IDC公布了第二季度最新排名&#xff0c;数据显示&#xff0c;海尔电脑消费类台式机在华北市场持续增长&#xff0c;连续两个季度再度排名亚军。华南市场同样取得了好成绩&#xff0c;台式机由第四上升至第三&#xff…

httpcilent绕过证书

2019独角兽企业重金招聘Python工程师标准>>> 对接其他公司接口&#xff0c;测试环境没有问题&#xff0c;生产环境出现https证书认证的问题&#xff0c; 网上搜了许久才发现一个&#xff0c;链接&#xff1a;http://pan.baidu.com/s/1dEDSmY1 密码&#xff1a;dpsb …

如何让apache支持.htaccess 解决Internal Server Error The server …错误

如何让apache支持.htaccess 解决Internal Server Error The server …错误 文章来源&#xff1a;小灰博客| 时间&#xff1a;2013-12-25 12:17:08| 作者&#xff1a;Leo | 2 条评论 文章分类&#xff1a;IT技术分享、PHP、小技巧 标签&#xff1a; .htaccess、apache 今天…

C# Linq源码解析之All

前言在Dotnet开发过程中&#xff0c;All作为IEnumerable的扩展方法&#xff0c;十分常用。本文对Aggregate方法的关键源码进行简要分析&#xff0c;以方便大家日后更好的使用该方法。使用确定序列中的所有元素是否都满足条件,如果都满足就返回true&#xff0c;如果有一个不满足…

(六)python3 只需3小时带你轻松入门——循环

for循环 使用循环可以重复执行某些代码&#xff0c;可以方便程序编写&#xff1b;但是不记效率的使用循环会使程序运行效率降低。 range 使用range()函数可以生成多个连续整数的range对象(这个概念后面会说)。基本格式&#xff1a;range(end)其中end是结尾数。range(10)则会生…

linux下查看mysql的当前连接情况

为什么80%的码农都做不了架构师&#xff1f;>>> 首先需要登录到mysql中。 总共有三个命令&#xff1a; 1、status mysql> status--------------mysql Ver 14.14 Distrib 5.5.30, for Linux (x86_64) using readline 5.1Connection id: 96Current data…

菜鸟学ASP.NET MVC4入门笔记

ASP.NET MVC 是微软官方提供的以MVC模式为基础的ASP.NET Web应用程序(Web Application)框架,它由Castle的MonoRail而来。 MVC 编程模式 MVC 是三种 ASP.NET 编程模式中的一种。 MVC 是一种使用 MVC(Model View Controller 模型-视图-控制器)设计创建 Web 应用程序的模式。 (…

vue 如何调用微信分享_微信jssdk分享接口,在vue单页应用使用中遇到的问题

微信jssdk分享接口&#xff0c;wx.updateAppMessageShareData&#xff0c;wx.updateTimelineShareData&#xff0c;在vue单页应用使用中遇到的问题&#xff0c;记录一下微信JS-SDK文档&#xff1a;原有的 wx.onMenuShareTimeline、wx.onMenuShareAppMessage、wx.onMenuShareQQ、…

用计算机画图软件画画教程,电脑画图软件有什么使用技巧,电脑画图软件教程...

一、第一步是点击右下角的“开始”图标。在开头上方&#xff0c;出现一个对话框&#xff0c;您找到“所有应用程序”&#xff0c;您右键单击鼠标&#xff0c;在出现的对话框中&#xff0c;您找到“附件”&#xff0c;您左键单击鼠标&#xff0c;在“附件”的右侧出现一个对话框…

JVM-并发-Java 内存模型

Java内存模型 (1). 主内存与工作内存 Java内存模型规定了所有的变量都存储在主内存中&#xff0e; 每类线程的变量的主内存副本拷贝&#xff0c;线程对变量的所有操作&#xff08;读操作&#xff0c;赋值操作等&#xff09;都必须工作内存中进行&#xff0c;而不能直接读写主内…

(七)python3 只需3小时带你轻松入门——List与dict

List列表 python中最基本的数据结构之一。序列&#xff08;或者说集合&#xff09;中的每个元素都分配一个数字用来表示它的位置&#xff08;索引&#xff09;&#xff0c;第一个索引是0&#xff0c;第二个索引是1&#xff0c;依此类推。 索引 索引最大值不能超过当前对象的最…

龙芯推出兼容IE的龙芯浏览器解决方案,全面支持ActiveX插件等应用类型

近期&#xff0c;龙芯中科推出了兼容IE的浏览器解决方案&#xff0c;可全面支持采用ActiveX插件等IE时代技术开发的网页应用。众所周知&#xff0c;IE浏览器因年代久远&#xff0c;已逐步退出历史舞台。但在我国行业和个人应用中&#xff0c;仍存在着大量基于IE浏览器开发的网页…

DPS软件做MK(Mann-Kendall)突变监测分析方法(附DPS 7.05软件下载地址)

Mann-Kendall是一种非参数统计检验方法&#xff0c;具有样本不遵从某一特定分布&#xff0c;不受个别异常值干扰&#xff0c;能够客观地表征样本序列整体变化趋势等优点。 虽然DPS软件具有强大的统计分析和数据可视化功能&#xff0c;但是相关的示例和教程却本博文演示Mann-Ke…

linux之ftp怎么把本地文件拷贝到服务端

1 问题 电脑本地文件怎么通过ftp拷贝到服务端 2 解决办法 我们可以用put命令 put file_path 那我们怎么解决把服务端的文件拷贝到本地呢&#xff1f;很明显啊&#xff0c;我们可以使用get命令 get file_path

clientHeight、offsetHeight 和 scrollHeight

2019独角兽企业重金招聘Python工程师标准>>> window.screen.availWidth 返回当前屏幕宽度(空白空间) window.screen.availHeight 返回当前屏幕高度(空白空间) window.screen.width 返回当前屏幕宽度(分辨率值) window.screen.height 返回当前屏幕…