我的动态页图片全都炸了!!
被标题吸引过来的吧,嗯?👀我已裂开 2026 年 7 月 26 日,周日,晚上。 我打开自己的博客想看看最近发的动态,点进动态页,屏幕上整齐地躺着一排裂图图标。一个、两个、三个……往下翻,全是裂的。 第一反应是 CDN 挂了。刷新,清缓存,换网络,图标依然坚挺地裂着。打开开发者工具看 Network,每张图片的请求都返回同一个干净利落的 404: {"message":"图片不存在"} 我愣了一下。这个错误信息我熟,是后端 imageProxy 接口写的。它说图片不存在,那就是图片真的不存在。 但问题是,这些动态我发了几个月了,一直好好的,怎么今天集体消失了? 现场勘查 SSH 登上服务器,直奔文件系统。 uploads/media 目录下,原本应该躺着一堆 .jpg .jpeg .png 原图。我 ls 了一下,顶层只剩一个孤零零的文件。再 ls compressed/,满满当当 1469 个文件安安静静地待在那儿。 原图没了,压缩版还在。 接着翻数据库。moments 表的 media_urls 字段存的是动态的图片 URL,我查了一条: [{"type":"photo","url":"/uploads/media/1777979411130-4sxla5.jpeg"}] 这个 URL 指向 uploads/media/1777979411130-4sxla5.jpeg。我回头去文件系统找这个文件——不存在。 案子到这里,轮廓已经清楚了:动态表里存的 URL 指向原图,原图被删了,所以 404。 可压缩版还好端端地在 compressed/ 目录里躺着呢。media 表里这些图片的 file_path 也早就更新成了压缩版路径。问题是,moments 表没人通知。谁给我家偷了,也不告我一声!? 排查嫌疑人 原图是谁删的? 服务器上有好几个清理脚本在跑。cleanup_media.cjs 每小时整点执行,cleanup_small_files.cjs 删小于 1KB 的文件,cleanup_malicious_uploads.cjs 删文件名以 upload 开头的恶意上传。 我一个个翻它们的日志。cleanup_media.cjs 的日志整整齐齐写着: Cleanup complete: 0 orphaned files (0.00MB freed), 0 old chunk dirs removed 一行一行,全是 0 ♀。另外两个脚本也排除了——我的动态图片一条都不沾它们的删除条件。 嫌疑洗不清,但也定不了罪🤔。文件确实没了,可没有一个脚本承认是自己干的。 缓存里的线索 线索卡住的时候,我盯着 thumbnails/moments 缓存目录看了很久。这个目录存着 imageProxy 生成的缩略图缓存,文件名是原 URL 路径转义来的。 6 月 21 日到 23 日生成的缓存,文件名长这样: _uploads_media_1777979411130_4sxla5_jpeg_w400.jpg 这说明那时候原图路径是有效的,imageProxy 能读到原图、生成缓存。 而我今天修复后新生成的缓存: _uploads_media_compressed_39_1777979411131_jpg_w400.jpg 路径前缀变了,从 uploads_media_xxx 变成了 uploads_media_compressed_xxx。 原图是在某个时间点之后才消失的。uploads/media 目录的 Modify 时间戳是今天 11:43。也就是说,今天上午十一点多,原图集体蒸发。 关键证据 既然文件层面找不到凶手,我决定回到代码本身。 git log 翻 transcodeService.js 的历史。服务器上 7 月 25 日还在跑的旧版本(commit da2867e),和今天部署的新版本,在图片转码这段代码上有一个根本性的区别。 旧版本的逻辑: 转码生成 compressed 副本 → 原图保留在 uploads/media/ 原位。 新版本的逻辑: 转码生成 compressed 副本 → fs.unlinkSync(absPath) 删掉原图 → 把 media 表的 file_path 更新为 compressed 路径。 注释里写得明明白白:Delete original to save disk space。为了省磁盘空间,删掉原图,让压缩版顶上。 这个想法没毛病,我也知道。但问题在于,它只更新了 media 表的 file_path,moments 表的 media_urls 没人管。 断链的瞬间,就在这里。 真相 把整条数据流捋一遍。 前端上传图片时,后端把原图路径返回给前端,前端把它存进 moment
(以上为文章预览,完整内容请访问本页面)