• Welcome to the world's largest Chinese hacker forum

    Welcome to the world's largest Chinese hacker forum, our forum registration is open! You can now register for technical communication with us, this is a free and open to the world of the BBS, we founded the purpose for the study of network security, please don't release business of black/grey, or on the BBS posts, to seek help hacker if violations, we will permanently frozen your IP and account, thank you for your cooperation. Hacker attack and defense cracking or network Security

    business please click here: Creation Security  From CNHACKTEAM

开发博客项目StarBlog NetCore-(4)批量导入Markdown博客


Recommended Posts

系列文章

开发博客项目Starblog。NetCore-(1)为什么需要自己写博客?

开发博客项目Starblog。NetCore-(2)环境准备和项目创建

开发博客项目StarBlog。NetCore-(3)模型设计

开发博客项目Starblog。NetCore-(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中创建一个新类:PostProcessor。迁移项目,我们将在这个类中实现与markdown文件相关的处理逻辑。

PostProcessor.cs的完整代码在这里:https://github.com/deali-axy/star blog/blob/master/star blog . migrate/post processor . cs

施工方法:

private readonly Post _ post

private只读string _ importPath

private只读string _ assetsPath

public Post processor(string import path,string assetsPath,Post post) {

_ post=post

_ assetsPath=assetsPath

_ importPath=importPath

}

在…之中

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

ImportPath:要导入的markdown文件夹路径。

AssetsPath:资源文件存储的路径,用于存储markdown中的图片。这个项目中设置的路径是StarBlog。网络/wwwroot/媒体/博客。

文章摘要提取

摘要提取,我做了一个简单的流程,把markdown内容渲染成文本,然后截取前N个单词组成一个摘要。代码如下:

公共字符串GetSummary(int length) {

回帖。内容==空

?字符串。空的

:降价。ToPlainText(_post。内容)。极限(长度);

}

文章状态和标题处理

我之前用本地markdown文件写博客的时候,出于个人习惯,在文件名前面加了一个前缀代表状态。例如,未完成的文章看起来像这样:

(未完)StarBlog博客开发笔记(四):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
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now