【Bug已解决】Codex Desktop Markdown preview does not render local images referenced by relative paths ...
【Bug已解决】Codex Desktop Markdown preview does not render local images referenced by relative paths 解决方案原始报错线索Codex Desktop Markdown preview does not render local images referenced by relative pathsMarkdown 预览器无法显示用相对路径引用的本地图片。一、现象长什么样作者写 Markdown 时用了相对路径图片预览里却看到图片位置是裂图控制台报Failed to load resource或Not allowed to load local resource同一份文件用别的编辑器如 Typora / VS Code打开图片正常显示唯独这个预览器不行把图片改成绝对路径又能显示了改成网络图也正常。 结论很明确预览器能加载绝对路径和网络资源唯独解析不了「相对路径」——问题出在「相对谁解析」。二、背景相对路径依赖「解析基址」2.1 什么是 base URLHTML / Markdown 里的相对路径images/arch.png必须有一个**基准地址base URL**才能算出绝对地址。基准来自Web 页面base href...或当前页面 URL 的目录本地文件被预览的.md文件所在目录桌面 WebViewWebView 加载的虚拟 URL如http://localhost/preview其目录是/preview而非文件真实目录。2.2 为什么桌面 WebView 特别容易踩坑很多桌面 Markdown 预览用内嵌 WebView 渲染 HTML。WebView 加载的可能是http://localhost:port/preview—— 此时相对路径基址是http://localhost:port/根本不包含你的./images/data:text/html,...—— 没有基址相对路径直接失效file:///tmp/preview.html—— 基址是/tmp/但你的 md 在~/doc/。 只要「WebView 加载地址的目录」≠「.md 文件真实目录」相对图片就裂。三、为什么相对路径图片裂了根因3.1 基址设置错误预览把 HTML 注入 WebView 时没把base hreffile:///真实/md/目录/写进head于是相对路径相对「虚拟预览地址」解析找不到文件。3.2 安全策略拦截file://WebView 默认禁止页面用file://加载本地资源防止任意网页读你磁盘。未显式放行file://访问图片就被拦成Not allowed to load local resource。3.3 路径未做 URL 编码 / 拼接错误中文目录、空格没编码成%20或拼接时多/少斜杠导致最终 URL 非法。3.4 相对计算用错 API直接用字符串拼接/./images/arch.png而非urljoin在多级../时算错。四、最小可运行复现Python 解析错误演示下面用urllib.parse.urljoin演示「基址选错→相对路径失效」from urllib.parse import urljoin # 情况 A基址是“md 文件真实目录” —— 正确 base_correct file:///Users/me/doc/ print(urljoin(base_correct, ./images/arch.png)) # file:///Users/me/doc/images/arch.png ✅ # 情况 B基址是“虚拟预览地址” —— 错误本 Bug 的根因 base_wrong http://localhost:8080/preview print(urljoin(base_wrong, ./images/arch.png)) # http://localhost:8080/images/arch.png ❌ 根本不在磁盘上 # 情况 C没有基址data: 注入 base_none data:text/html, print(urljoin(base_none, ./images/arch.png)) # data:images/arch.png ❌ 完全失效运行即看到基址一错相对路径就指向了不存在的地方。五、解决方案一注入正确的base href渲染 Markdown 成 HTML 时把.md文件目录作为 base 写进headimport os from urllib.parse import urljoin def render_markdown_html(md_path: str, html_body: str) - str: # 真实目录 - file:// URL注意末尾斜杠 dir_url file:// os.path.abspath(os.path.dirname(md_path)) / return f!DOCTYPE html html head meta charsetutf-8 base href{dir_url} /head body {html_body} /body /html if __name__ __main__: md /Users/me/doc/readme.md body img src./images/arch.png out render_markdown_html(md, body) print(out) # base hreffile:///Users/me/doc/ 让 ./images/arch.png 正确解析base href一设页面里所有相对路径图片、链接、CSS都相对 md 目录解析裂图消失。六、解决方案二用 urljoin 做安全的相对解析不依赖 base 标签如果不用 WebView 的 base而是在后端把相对路径预先解析成绝对路径再注入import os from urllib.parse import urljoin def resolve_image_src(md_dir: str, src: str) - str: 把 Markdown 里的图片 src 解析成可用的绝对地址。 if src.startswith((http://, https://, data:)): return src # 网络/内联图原样 if src.startswith(file://): return src # 已是绝对文件 if os.path.isabs(src): return file:// src if not src.startswith(file://) else src # 相对路径以 md 目录为基址解析 base file:// md_dir / return urljoin(base, src) if __name__ __main__: md_dir /Users/me/doc print(resolve_image_src(md_dir, ./images/arch.png)) # file:///Users/me/doc/images/arch.png print(resolve_image_src(md_dir, ../assets/logo.png)) # file:///Users/me/assets/logo.png 正确处理了 ../ print(resolve_image_src(md_dir, https://x.com/a.png)) # https://x.com/a.png 网络图原样urljoin自动处理./、../、多级回退比手写字符串拼接可靠得多。七、解决方案三放开 WebView 的本地文件访问安全前提下桌面 WebView 默认拦截file://。需要在「预览本地 md」这一受控场景下显式放行同时做好边界限制# 概念示例以通用 WebView 设置项表达非某个私有 API WEBVIEW_SETTINGS { allow_file_access: True, # 允许读本地文件 allow_file_access_from_file_urls: True, # 允许 file:// 页面读 file:// allow_universal_access_from_file_urls: False, # 不放开到任意源 } def is_safe_local_path(md_dir: str, target: str) - bool: 校验解析后的图片确实在 md 目录子树内防止 ../ 逃逸到系统目录。 import os real_md os.path.realpath(md_dir) real_target os.path.realpath(target) return os.path.commonpath([real_md, real_target]) real_md要点放开访问的同时用commonpath约束图片只能落在 md 目录及其子目录防止恶意 md 用../../etc/passwd之类读取系统文件。八、跨平台注意点8.1 路径分隔符Windows 是\拼file://时要转成/C:\a\b→file:///C:/a/b注意三个斜杠协议 空 authority 盘符。def to_file_url(path: str) - str: p os.path.abspath(path).replace(\\, /) if p[1] :: # Windows 盘符 C:/... return file:/// p return file:// p8.2 中文 / 空格编码file://里的非 ASCII 与空格应编码urllib.parse.quote否则部分 WebView 不认。8.3 与网络图共存http(s)://与data:内联图要保持原样第六节已处理不要试图把它们当本地文件解析。8.4 缓存图片更新后预览不刷新多半是 WebView 缓存。可给图片 URL 加?vmtime戳强制刷新。九、排查清单Markdown 相对图片裂图按下面排查先改成绝对路径能显示说明是「相对解析」问题不是图本身坏了检查预览 HTML 是否写了base href没写就是基址错看 WebView 加载地址是file://还是http://localhost还是data:基址要对应 md 目录控制台是否报Not allowed to load local resource是则需在受控下放开 file 访问相对路径用什么解析用urljoin而非字符串拼接路径编码中文 / 空格是否%20编码安全边界放开 file 访问时用commonpath限制只能读 md 子树Windows 盘符file:///C:/...三个斜杠别写错。十、小结「Markdown 预览不渲染相对路径本地图片」的根因几乎总是解析基址base URL选错或 WebView 安全策略拦截file://预览器把 HTML 注入虚拟地址相对路径却去了一个不存在的地方找图。通用修复注入正确base hreffile://md目录/让所有相对资源相对 md 目录解析后端预解析用urljoin把相对 src 算成绝对file://再注入正确处理./与../受控放开 file 访问WebView 允许file://的同时用commonpath把读取范围锁死在 md 子树内防止路径逃逸跨平台细节Windows 三个斜杠、中文空格编码、网络图原样保留。 记住相对路径永远相对某个基址预览器要渲染本地图基址就必须是「被预览文件真实所在目录」而不是「虚拟预览页地址」。这一条想通裂图问题迎刃而解。
