8

基于.NetCore开发博客项目 StarBlog - (18) 实现本地Typora文章打包上传 - 程序设计实...

 1 year ago
source link: https://www.cnblogs.com/deali/p/16758878.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

基于.NetCore开发博客项目 StarBlog

九月太忙,只更新了三篇文章,本来这个功能是从九月初就开始做的,结果一直拖到现在国庆假期才有时间完善并且写文章~

之前我更新了几篇关于 Python 的文章,有朋友留言问是不是不更新 .Net 了,那肯定不能啊,我只能说「我 全 都 要」,所以我反手就更新了一篇Asp-Net-Core开发笔记

然后顺便立个Flag:今年底前完成StarBlog系列文章的主体部分(即API开发+后台前端开发,目前只完成博客前后端部分),加油吧~

OK,说回本文,程序员都喜欢用Markdown来写文章,但由于markdown是纯文本格式,在其中插入的图片要如何保存,就成了一大烦恼,有人选择图床,但不一定永久有效;有人选择本地存储,图片永久有效,但如何分享文章又成了一个难题…

我选的就是第二种,本地存储。使用Typora写文章,图片保存在和Markdown文件同名的目录(markdown.assets)下,这样可以获得很好的写作体验,然后分享的问题就交给StarBlog吧,这个项目开发的初衷就是为了把本地的文章发表成博客。

不过之前只有批量导入文章的功能,现在我要做的就是单独实现一个单篇文章打包导入的功能。

随着文章越来越多,系列文章的目录放前面有点影响阅读了,所以从这篇开始我把它放到最后面~

假设我用Typora写了一篇Markdown文章,文件名为:StarBlog.md,并且在里面插入了若干图片,根据配置,Typora会自动生成一个目录(StarBlog.assets)来存放这些图片。

为了实现导入,我要把这个markdown文件和这个存图片的目录一起打包成zip压缩文件上传,后端将zip压缩包解压到临时目录,读取Markdown文件,解析其中的内容,进行导入操作。

OK,开始写代码吧

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

首先是解压缩功能,.Net标准库自带 ZipFile 这个库用于操作zip压缩包,在 System.IO.Compression 里,直接用就完事了。

解压前得先把文件复制到临时目录,并创建一个新的临时目录来放解压后的文件。

Services/BlogServices.cs 里新增代码

public async Task<Post> Upload(PostCreationDto dto, IFormFile file) {
    // 先复制到临时文件
    var tempFile = Path.GetTempFileName();
    await using (var fs = new FileStream(tempFile, FileMode.Create)) {
        await file.CopyToAsync(fs);
    }

    // 设定解压用的临时目录
    var extractPath = Path.Combine(Path.GetTempPath(), "StarBlog", Guid.NewGuid().ToString());
  
    // 使用 GBK 编码解压,防止中文文件名乱码
    Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
    ZipFile.ExtractToDirectory(tempFile, extractPath, Encoding.GetEncoding("GBK"));
}

本来直接 ZipFile.ExtractToDirectory() 就能解压了,但如果压缩包里的文件用了中文名,就得先设置编码。

解析Markdown

关于 C# 解析 Markdown ,在本系列一开始就写过,所以这里就不再当复读机了,可以直接看这两篇文章:

直接上代码了

因为是做单篇文章导入,所以我这里获取临时目录写的所有 *.md 文件之后只取第一个文件来处理(理论上也不应该有多个~)

var dir = new DirectoryInfo(extractPath);
var files = dir.GetFiles("*.md");
var mdFile = files.First();
using var reader = mdFile.OpenText();
var content = await reader.ReadToEndAsync();
var post = new Post {
  Id = GuidUtils.GuidTo16String(),
  Status = "已发布",
  Title = dto.Title ?? $"{DateTime.Now.ToLongDateString()} 文章",
  IsPublish = true,
  Content = content,
  Path = "",
  CreationTime = DateTime.Now,
  LastUpdateTime = DateTime.Now,
  CategoryId = dto.CategoryId,
};

var assetsPath = Path.Combine(_environment.WebRootPath, "media", "blog");
var processor = new PostProcessor(extractPath, assetsPath, post);

// 处理文章标题和状态
processor.InflateStatusTitle();

// 处理文章正文内容
// 导入文章的时候一并导入文章里的图片,并对图片相对路径做替换操作
post.Content = processor.MarkdownParse();
post.Summary = processor.GetSummary(200);

Markdown相关的处理,我封装了 PostProcessor 这个对象,在 StarBlog.Share 里。

处理多级分类

如果文章的分类不是一级分类,那么把它上面的所有分类找出来,一个个排好队,方便后面处理。

// 处理多级分类
var category = await _categoryRepo.Where(a => a.Id == dto.CategoryId).FirstAsync();
if (category == null) {
  post.Categories = "0";
}
else {
  var categories = new List<Category> {category};
  var parent = category.Parent;
  while (parent != null) {
    categories.Add(parent);
    parent = parent.Parent;
  }

  categories.Reverse();
  post.Categories = string.Join(",", categories.Select(a => a.Id));
}
// 存入数据库
post = await _postRepo.InsertAsync(post);

__EOF__


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK