Solo 导入 Markdown 文章

本贴最后更新于 2119 天前,其中的信息可能已经时过境迁

本文是《Solo 从设计到实现》的一个章节,该系列文章将介绍 Solo 这款 Java 博客系统是如何从无到有的,希望大家能通过它对 Solo 从设计到实现有个直观地了解、能为想参与贡献的人介绍清楚项目,也希望能为给重复发明重新定义博客系统的人做个参考 ❤️

如果你之前使用过静态博客系统(比如 Hexo、Jekyll),Solo 可以很方便地将你已有的 Markdown 文件进行导入,并且在最大程度上保留以前的元信息,比如发布时间、标签、自定义链接等。

导入时机

因为导入操作不是必要且高频的操作,所以设计上为了保持简单,我们选择在启动时扫描固定路径 markdowns 文件夹,然后将该文件夹下所有 *.md 文件进行解析并作为文章导入。

文件夹路径获取:

final ServletContext servletContext = SoloServletListener.getServletContext();
final String markdownsPath = servletContext.getRealPath("markdowns");

导入主逻辑:

final Collection<File> mds = FileUtils.listFiles(new File(markdownsPath), new String[]{"md"}, true);
if (null == mds || mds.isEmpty()) {
    return;
}

for (final File md : mds) {
    final String fileName = md.getName();
    if (StringUtils.equalsIgnoreCase(fileName, "README.md")) {
        continue;
    }

    try {
        final String fileContent = FileUtils.readFileToString(md, "UTF-8");
        final JSONObject article = parseArticle(fileName, fileContent);
        article.put(Article.ARTICLE_AUTHOR_ID, adminId);

        final JSONObject request = new JSONObject();
        request.put(Article.ARTICLE, article);

        final String id = articleMgmtService.addArticle(request);
        FileUtils.moveFile(md, new File(md.getPath() + "." + id));
        LOGGER.info("Imported article [" + article.optString(Article.ARTICLE_TITLE) + "]");
        succCnt++;
    } catch (final Exception e) {
        LOGGER.log(Level.ERROR, "Import file [" + fileName + "] failed", e);

        failCnt++;
        failSet.add(fileName);
    }
}

导入结束后原 md 文件将被重命名为 .md.{时间毫秒} 这样的格式,如不需要,可将这类后缀的文件删除。

文件解析

每个 md 文件都会按照 Hexo/Jekyll 定义的头部进行解析,以确定标题、标签等:

  • Hexo 头
  • Jekyll 头
  • 支持头信息中使用 descriptionsummaryabstract 作为文章摘要,如果没有的话将自动截取正文部分
  • 如果没有定义头信息,或者解析失败,则以文件名作为标题、Note 作为标签、当前时间作为发布时间进行导入,这也是导入普通 md 文件的规则

Hexo/Jekyll 的文件头是使用 Yaml 格式的,我们使用 org.yaml.snakeyaml 库进行解析:

fileContent = StringUtils.trim(fileContent);
String frontMatter = StringUtils.substringBefore(fileContent, "---");
if (StringUtils.isBlank(frontMatter)) {
    fileContent = StringUtils.substringAfter(fileContent, "---");
    frontMatter = StringUtils.substringBefore(fileContent, "---");
}

final JSONObject ret = new JSONObject();
final Yaml yaml = new Yaml();
Map elems;
try {
    elems = (Map) yaml.load(frontMatter);
} catch (final Exception e) {
    // treat it as plain markdown
    ret.put(Article.ARTICLE_TITLE, StringUtils.substringBeforeLast(fileName, "."));
    ret.put(Article.ARTICLE_CONTENT, fileContent);
    ret.put(Article.ARTICLE_ABSTRACT, Article.getAbstract(fileContent));
    ret.put(Article.ARTICLE_TAGS_REF, DEFAULT_TAG);
    ret.put(Article.ARTICLE_IS_PUBLISHED, true);
    ret.put(Article.ARTICLE_COMMENTABLE, true);
    ret.put(Article.ARTICLE_VIEW_PWD, "");

    return ret;
}

容错处理

解析时难免会遇到一些“不按套路出牌”的内容,为了尽量兼容各种情况,我们得做一些容错处理:

  • 缺少标题则按文件名作为标题
  • 兼容各种日期格式:yyyy/MM/dd HH:mm:ssyyyy-MM-dd HH:mm:ssdd/MM/yyyy HH:mm:ss 等,解析失败则以当前时间作为文章创建时间
  • 抽取摘要:依次获取 descriptionsummaryabstract,如果都没有则按默认规则抽取
  • 解析标签:依次获取 tagscategorycategorieskeywordkeywords,如果都没有则默认添加 Note 作为标签

所以,理论上即使不是 Hexo/Jekyll 的 md 文件,只是普通 md 文件也是可以导入 Solo 的。

  • Solo

    Solo 是一款小而美的开源博客系统,专为程序员设计。Solo 有着非常活跃的社区,可将文章作为帖子推送到社区,来自社区的回帖将作为博客评论进行联动(具体细节请浏览 B3log 构思 - 分布式社区网络)。

    这是一种全新的网络社区体验,让热爱记录和分享的你不再感到孤单!

    1433 引用 • 10052 回帖 • 483 关注
  • 设计
    113 引用 • 797 回帖 • 1 关注
  • 文档
    56 引用 • 1289 回帖 • 2 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • Mr-YH

    有没有教程,这个代码看不懂

    1 回复
  • 其他回帖
  • PeterChu

    😄 嗯嗯,D 执行力真牛啊,感谢。我提的意见有时角度不同,或者其他因素,或许考虑不周,希望酌情采纳即可。

  • kuteng

    已开始我被 Solo 吸引了。不过进一步了解后,准备放弃 Solo。
    因为通过上传一个 Makedown 文件,发布或更新一篇博文是我的硬性需求。因为我懒。
    我不想切换到博客的编辑页面去写博客。我只希望通过 Typora 等工具,维护一个充满 Makedown 文件的文件夹。有新修改的时候就执行一个命令将相关文件上传到博客系统进行更新。(通过 Hexo 等格式,可以该博客是更新还是新增,博客 ID 等信息)。

    1 回复
  • 查看全部回帖