Solo 导入 Markdown 文章

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

本文是《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 构思 - 分布式社区网络)。

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

    1434 引用 • 10054 回帖 • 489 关注
  • 设计
    114 引用 • 797 回帖 • 1 关注
  • 文档
    56 引用 • 1289 回帖 • 2 关注

相关帖子

欢迎来到这里!

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

注册 关于
请输入回帖内容 ...