前言

昨天在发布那篇《在鸡哥14x上安装Linux:Fedora 42 上手体验》的文章时

(使用 StarBlog Publisher 发布文章时)发现其中的 webp 图片都没法自动上传

一开始我以为是没法识别 webp 格式

关于 StarBlog Publisher 工具,这是一款面向 StarBlog 的跨平台 Markdown 文章发布工具,内置多种主流大模型智能助手,支持即写即发和直观内容管理,助力高效创作。

项目开源地址: https://github.com/star-blog/starblog-publisher

更多信息可以查看这些文章:

问题解决

后来写了几个 demo 测试后才发现,是因为文件名里有空格,而 Markdig 很严格,不支持识别带空格的文件名😂,要么就是在插入图片时(Typora 里)先转义,要么就得自己处理图片解析。

文章里的图片是这样的格式

![](./2025-09-07-在鸡哥14x上安装使用Fedora42.assets/截图 2025-09-07 21-14-46.webp)

后面我修改了 starblog-publisher 分析图片的方法

做了以下改进:

  • 使用正则表达式 @"!\[([^\]]*)\]\(([^)]+)\)" 匹配图片语法
  • 支持路径中包含空格、中文字符等特殊字符
  • 自动处理引号包围的路径和 URL 编码

代码

话不多说,直接上代码吧

先看原来的图片解析代码,完全使用 Markdig 来实现

/// <summary>
/// 从Markdown内容中提取所有图片路径
/// </summary>
/// <returns>图片路径数组</returns>
public string[] ExtractImagePaths() {
    if (post.Content == null) {
        return Array.Empty<string>();
    }

    var document = Markdig.Markdown.Parse(post.Content);
    var imagePaths = new List<string>();
    var baseDir = Path.GetDirectoryName(filepath) ?? "";

    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 (string.IsNullOrWhiteSpace(linkInline.Url)) continue;

            var imgUrl = Uri.UnescapeDataString(linkInline.Url);

            // 如果是本地图片路径,转换为绝对路径
            if (!imgUrl.StartsWith("http")) {
                // 规范化路径
                imgUrl = imgUrl.Replace('/', Path.DirectorySeparatorChar) // 统一路径分隔符
                    .Replace(".\\\\", "") // 移除相对路径前缀
                    .Replace("./", ""); // 移除相对路径前缀

                imgUrl = Path.GetFullPath(Path.Combine(baseDir, imgUrl));
                imagePaths.Add(imgUrl);
            }
        }
    }

    return imagePaths.ToArray();
}

自行实现图片解析

但 Markdig 实在是拉胯

那就只能我自己来实现解析了

也不难,用正则就可以了

/// <summary>
/// 提取带空格的图片路径(Markdig 无法正确解析的情况)
/// </summary>
/// <param name="content">Markdown 内容</param>
/// <param name="baseDir">基础目录</param>
/// <returns>图片路径列表</returns>
private List<string> ExtractImagePathsWithSpaces(string content, string baseDir) {
    var imagePaths = new List<string>();

    // 使用正则表达式匹配图片语法:![alt](path)
    // 支持路径中包含空格、中文字符等
    var imagePattern = @"!\[([^\]]*)\]\(([^)]+)\)";
    var matches = System.Text.RegularExpressions.Regex.Matches(content, imagePattern);

    foreach (System.Text.RegularExpressions.Match match in matches) {
        if (match.Groups.Count >= 3) {
            var imagePath = match.Groups[2].Value.Trim();

            // 移除可能的引号
            if ((imagePath.StartsWith('"') && imagePath.EndsWith('"')) ||
                (imagePath.StartsWith('\'') && imagePath.EndsWith('\''))) {
                imagePath = imagePath.Substring(1, imagePath.Length - 2);
            }

            // URL 解码
            imagePath = Uri.UnescapeDataString(imagePath);

            // 只处理本地路径
            if (!imagePath.StartsWith("http")) {
                // 规范化路径
                imagePath = imagePath.Replace('/', Path.DirectorySeparatorChar)
                    .Replace(".\\\\", "")
                    .Replace("./", "");

                var fullPath = Path.GetFullPath(Path.Combine(baseDir, imagePath));
                imagePaths.Add(fullPath);
            }
        }
    }

    return imagePaths;
}

补充一下原方法

然后再补充一下原来 ExtractImagePaths 方法的代码

把底部的代码改成这样

// 添加自定义解析逻辑处理带空格的图片路径
// Markdig 无法正确解析带空格的图片路径,需要手动处理
var customPaths = ExtractImagePathsWithSpaces(post.Content, baseDir);
imagePaths.AddRange(customPaths);

// 去重并返回
return imagePaths.Distinct().ToArray();

实现效果

问题解决之后,可以正常识别到所有图片了

之前我在 starblog-publisher 里做了个画廊功能,可以很方便看到文章里的所有图片

image-20250908122447207

存在问题

使用正则表达式虽然简单粗暴,但有时会过于灵敏了

比如本文贴的代码里的 // 使用正则表达式匹配图片语法:![alt](path) 这一行注释,也会被识别到

就很烦,解决方法只能暂时是做成开关,由用户决定是否开启正则识别

只有当 Markdig 无法识别的情况下,才开启。然后再加一个忽略不存在的图片来解决。

开关

说干就干,我在设置里增加了一个开关

<!-- 图片解析设置卡片 -->
<Border Classes="Card">
    <StackPanel Spacing="15" Margin="15">
        <StackPanel Orientation="Horizontal" Spacing="8">
            <i:Icon Value="fa-solid fa-image" Foreground="#FF9800" />
            <TextBlock Text="图片解析设置" FontWeight="Bold" FontSize="16" />
        </StackPanel>

        <StackPanel Spacing="10">
            <ToggleSwitch OnContent="启用正则识别" OffContent="仅使用标准识别"
                          IsChecked="{Binding EnableRegexImageParsing}" />
            <TextBlock Text="启用后将使用正则表达式额外识别带空格的图片路径,但可能会过度识别。建议仅在需要处理特殊路径时开启。" 
                       TextWrapping="Wrap" 
                       FontSize="12" 
                       Foreground="{DynamicResource SystemBaseMediumColor}" 
                       Margin="0,5,0,0" />
        </StackPanel>
    </StackPanel>
</Border>

效果是这样的

image-20250908124055845

不开启的情况下,识别出本文的图片就是这样的

image-20250908124224250

开启之后是这样,不存在的图片,我也加了标注

image-20250908124158289

跳过不存在的

在 StarBlogPublisher/Services/MarkdownProcessor.cs 的 MarkdownParse 方法里添加代码

// 检查文件是否存在,跳过不存在的图片
if (!File.Exists(imgUrl)) {
    Console.WriteLine($"跳过不存在的图片文件: {imgUrl}");
    continue;
}

小结

做一个好用的工具,就是得不断迭代优化

不过我还是得吐槽一下,Markdig 像个毛坯房一样🤣