返回文章列表文章索引阅读全文
STARBLOG / 阅读手记
StarBlog博客开发笔记

StarBlog Development Notes (18): Implementing Packaged Upload of Local Typora Articles

Preface September was too busy; I only published three articles. I originally started working on this feature in early September, but it kept getting delayed until the National Day holiday when I finally had time to finish it and write this post~ I previously published a few articles about Python, and some friends left comments asking if I had stopped updating .Net. Of course not! I can only say**「I want it ALL」**, so I immediately followed up with an Asp-Net-Core development notes post. Then I’ll casually set a Flag: finish the main part of the StarBlog series by the end of this year (i.e., API development + admin front

曦远 (DealiAxy)约 22 分钟
目录9 个章节

Preface

September was too busy; I only published three articles. I originally started working on this feature in early September, but it kept getting delayed until the National Day holiday when I finally had time to finish it and write this post~

I previously published a few articles about Python, and some friends left comments asking if I had stopped updating .Net. Of course not! I can only say**「I want it ALL」**, so I immediately followed up with an Asp-Net-Core Development Notes post.

Then I’ll casually set a Flag: finish the main part of the StarBlog series by the end of this year (i.e., API development + admin frontend development; currently only the blog frontend and backend are done). Let’s go~

OK, back to this article. Programmers all like writing articles in Markdown, but since markdown is a plain-text format, how to save the images inserted in it becomes a major headache. Some people choose image hosting, but it may not remain valid forever; others choose local storage, where images remain valid forever, but then how to share the article becomes another problem…

I chose the second option: local storage. I write articles with Typora, and images are saved in a directory with the same name as the Markdown file (markdown.assets), which gives a great writing experience. Then the sharing problem is left to StarBlog—the original purpose of this project was to publish local articles as a blog.

Previously there was only a batch import feature for articles; now what I need to do is separately implement a packaged import feature for a single article.

As there are more and more articles, putting the series table of contents at the front is starting to affect reading, so starting from this post I’ll put it at the very end~

Implementation Approach

Suppose I write a Markdown article with Typora, with the filename: StarBlog.md, and insert several images in it. Based on the configuration, Typora will automatically generate a directory (StarBlog.assets) to store these images.

To implement import, I need to package this markdown file together with this image directory into a zip archive and upload it. The backend extracts the zip archive to a temporary directory, reads the Markdown file, parses its contents, and performs the import.

Code Implementation

OK, let’s start writing code.

Meanwhile, all project code has been uploaded to GitHub. Stars/Forks from all of you are welcome!

Unzipping

First is the unzipping feature. The .Net standard library comes with ZipFile for working with zip archives, in System.IO.Compression—just use it and you’re done.

Before extracting, you need to copy the file to a temporary directory first, and create a new temporary directory to hold the extracted files.

Add the following code in 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"));
}

Originally, just calling ZipFile.ExtractToDirectory() would be enough to extract, but if files in the archive use Chinese names, you have to set the encoding first.

Parsing Markdown

As for parsing Markdown in C#, I already wrote about it at the start of this series, so I won’t be a broken record here. You can check out these two articles directly:

Here’s the code.

Since this is a single-article import, after getting all *.md files in the temporary directory I only take the first file to process (in theory there shouldn’t be more than one anyway~)

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);

For Markdown-related processing, I encapsulated a PostProcessor object, in StarBlog.Share.

Handling Multi-level Categories

If the article’s category is not a first-level category, find all the categories above it and line them up one by one, to make later processing easier.

// 处理多级分类
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));
}

Finally, Save

Done~

// 存入数据库
post = await _postRepo.InsertAsync(post);

Series Articles

References

_EOF_

StarBlog Development Notes (18): Implementing Packaged Upload of Local Typora Articles

本文作者:
程序设计实验室
版权声明:
本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

相关文章

讨论

共 0 条

良言一句三冬暖,恶语伤人六月寒。

加载评论中…