☰
同一篇稿子发到 6 个平台,为什么只有这里的图片全裂了?
2026/10/12 5:36:45 网站建设 项目流程

同一篇稿子发到 6 个平台,为什么只有这里的图片全裂了?

一稿多发最尴尬的瞬间:文章发出去 10 分钟,读者在评论区问「你的图呢?」

排查之后你会发现,问题往往不在你的 Markdown,而在图床和 Referer 校验。

本文把「多平台发文时图片为什么会裂」这件事讲清楚,并给出一套可以落地的处理方案。


现象:同一份源文件,三种结果

把同一份 Markdown 分发到不同平台,图片的表现通常分三类:

表现典型原因
图片正常平台站外图片不做来源校验,或自己的图床可以被外链
图片自动转存后正常平台抓取外链图,上传到自己的图床后再替换链接
图片全裂源图床对Referer做了校验,站外请求一律 403

第三类最容易被误判成「Markdown 写错了」。实际上链接是完全正确的,只是服务端拒绝了这次请求。


根因:Referer 校验

很多图床(尤其是云厂商对象存储的默认域名)会做防盗链:

GET /direct/xxxx.png Referer: https://juejin.cn/ → 403 Forbidden

而同一个地址,如果Referer是它自己的域名,就返回 200。

也就是说:图片能不能显示,取决于「谁在请求它」。

这也解释了一个很常见的困惑——「我本地预览明明是好的」:本地文件或者同域引用根本不会带上触发校验的Referer。

一条命令验证

排查第一步,先确认是不是来源校验:

# 不带 Referercurl-s-o/dev/null-w"%{http_code}\n""https://example-cdn.com/direct/abc.png"# 带上目标平台的 Referercurl-s-o/dev/null-w"%{http_code}\n"\-H"Referer: https://juejin.cn/"\"https://example-cdn.com/direct/abc.png"

两个结果不一致(200 / 403),基本可以定位就是防盗链。


陷阱:懒加载会让「自动检测」给出假阴性

很多人用「图片加载失败」来判断裂图,写法大致是:

constbad=[...document.images].filter(img=>img.naturalWidth===0);

这个判断在懒加载面前是失真的:还没滚动到的图片,naturalWidth也是 0,但它并没有裂。

更可靠的做法是看src的最终形态:

constimgs=[...document.querySelectorAll('.article-content img')];consthosts=imgs.map(i=>{try{returnnewURL(i.currentSrc||i.src).host;}catch{return'INVALID';}});console.table(hosts);

判断标准变成两条:

  1. src是否指向该平台自己的图床域名(转存成功);
  2. 是否仍是原始外链域名(可能被拒)。

对站点自己的域名,再补一次 HTTP 探测,就能得到确定结论,而不是靠naturalWidth猜。


解决方案:从「一份稿子」到「每平台一份」

方案一:发布前批量转存(推荐)

思路很简单:目标平台自己的图床,才是唯一不会 403 的来源。

流程:

解析 Markdown 中的图片链接 → 下载到本地临时目录 → 上传到目标平台图床 → 用返回的新链接替换原文 → 再执行发布

用 Node 实现核心两步并不复杂:

// 1) 下载源图,保留原始扩展名asyncfunctiondownload(url,dir){constres=awaitfetch(url);if(!res.ok)thrownewError(`download${res.status}:${url}`);constext=(newURL(url).pathname.match(/\.(png|jpe?g|gif|webp)$/i)||['.png'])[0];constfile=path.join(dir,crypto.randomUUID()+ext);awaitfs.promises.writeFile(file,Buffer.from(awaitres.arrayBuffer()));returnfile;}// 2) 串行转存,避免把平台图床打限流for(constlinkoflinks){constlocal=awaitdownload(link,tmpDir);constuploaded=awaituploadToPlatform(local);// 各平台接口/交互不同md=md.replaceAll(link,uploaded);}

两个实践要点:

  • 串行,不要并发。上传接口大多有限流,并发只会换来一批 429;
  • 保留扩展名。不少平台的图床按后缀判断Content-Type,丢了后缀会变成下载而不是显示。

方案二:为每个平台维护一个「图床版本」

如果平台数量固定、发文频率高,更省事的做法是:一份内容,多份链接版本。

jjmd/article.md # 通用版(用平台 A 的图床) c51md/article.md # 51CTO 版(必须用站内图床)

用脚本维护一次映射表,之后每次发文只替换图片链接前缀即可。代价是要多维护几份文件,好处是发布环节零风险、零等待。


把「图片可用性」做成发布前的断言

最后一件事,也是最能省时间的:把检查前置到发布之前。

functionassertImagesResolvable(md,allowedHosts){constlinks=[...md.matchAll(/!\[[^\]]*\]\(([^)]+)\)/g)].map(m=>m[1]);constbad=links.filter(l=>{try{return!allowedHosts.includes(newURL(l).host);}catch{returntrue;}});if(bad.length){thrownewError(`以下图片不在允许的图床内,发布前必须转存:\n${bad.join('\n')}`);}}

允许域名列表由各平台决定:站内图床 + 明确支持外链的域名,其余一律拦下。

这样做的价值不是「多检查一步」,而是把发布后才发现的问题提前到发布前必然发现——后者是可以自动修复的,前者只能重发。


小结

  • 图片裂不裂,取决于图床的 Referer 校验,跟 Markdown 写法无关;
  • 用curl对比带/不带Referer的返回码,一秒定性;
  • 别用naturalWidth判断裂图,懒加载会骗你;
  • 想彻底解决,就把图片转存到目标平台自己的图床,并串行执行;
  • 最后,把图床域名白名单做成发布前断言。

一稿多发真正的工作量,从来不在写,而在适配。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询