同一篇稿子发到 6 个平台,为什么只有这里的图片全裂了?
同一篇稿子发到 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}\nhttps://example-cdn.com/direct/abc.png# 带上目标平台的 Referercurl-s-o/dev/null-w%{http_code}\n\-HReferer: https://juejin.cn/\https://example-cdn.com/direct/abc.png两个结果不一致200 / 403基本可以定位就是防盗链。陷阱懒加载会让「自动检测」给出假阴性很多人用「图片加载失败」来判断裂图写法大致是constbad[...document.images].filter(imgimg.naturalWidth0);这个判断在懒加载面前是失真的还没滚动到的图片naturalWidth也是 0但它并没有裂。更可靠的做法是看src的最终形态constimgs[...document.querySelectorAll(.article-content img)];consthostsimgs.map(i{try{returnnewURL(i.currentSrc||i.src).host;}catch{returnINVALID;}});console.table(hosts);判断标准变成两条src是否指向该平台自己的图床域名转存成功是否仍是原始外链域名可能被拒。对站点自己的域名再补一次 HTTP 探测就能得到确定结论而不是靠naturalWidth猜。解决方案从「一份稿子」到「每平台一份」方案一发布前批量转存推荐思路很简单目标平台自己的图床才是唯一不会 403 的来源。流程解析 Markdown 中的图片链接 → 下载到本地临时目录 → 上传到目标平台图床 → 用返回的新链接替换原文 → 再执行发布用 Node 实现核心两步并不复杂// 1) 下载源图保留原始扩展名asyncfunctiondownload(url,dir){constresawaitfetch(url);if(!res.ok)thrownewError(download${res.status}:${url});constext(newURL(url).pathname.match(/\.(png|jpe?g|gif|webp)$/i)||[.png])[0];constfilepath.join(dir,crypto.randomUUID()ext);awaitfs.promises.writeFile(file,Buffer.from(awaitres.arrayBuffer()));returnfile;}// 2) 串行转存避免把平台图床打限流for(constlinkoflinks){constlocalawaitdownload(link,tmpDir);constuploadedawaituploadToPlatform(local);// 各平台接口/交互不同mdmd.replaceAll(link,uploaded);}两个实践要点串行不要并发。上传接口大多有限流并发只会换来一批 429保留扩展名。不少平台的图床按后缀判断Content-Type丢了后缀会变成下载而不是显示。方案二为每个平台维护一个「图床版本」如果平台数量固定、发文频率高更省事的做法是一份内容多份链接版本。jjmd/article.md # 通用版用平台 A 的图床 c51md/article.md # 51CTO 版必须用站内图床用脚本维护一次映射表之后每次发文只替换图片链接前缀即可。代价是要多维护几份文件好处是发布环节零风险、零等待。把「图片可用性」做成发布前的断言最后一件事也是最能省时间的把检查前置到发布之前。functionassertImagesResolvable(md,allowedHosts){constlinks[...md.matchAll(/!\[[^\]]*\]\(([^)])\)/g)].map(mm[1]);constbadlinks.filter(l{try{return!allowedHosts.includes(newURL(l).host);}catch{returntrue;}});if(bad.length){thrownewError(以下图片不在允许的图床内发布前必须转存\n${bad.join(\n)});}}允许域名列表由各平台决定站内图床 明确支持外链的域名其余一律拦下。这样做的价值不是「多检查一步」而是把发布后才发现的问题提前到发布前必然发现——后者是可以自动修复的前者只能重发。小结图片裂不裂取决于图床的 Referer 校验跟 Markdown 写法无关用curl对比带/不带Referer的返回码一秒定性别用naturalWidth判断裂图懒加载会骗你想彻底解决就把图片转存到目标平台自己的图床并串行执行最后把图床域名白名单做成发布前断言。一稿多发真正的工作量从来不在写而在适配。