修复:图标存储未跟随后台配置,本地模式下图标显示不出来

用户指出图标应当与附件同一套逻辑:管理员在后台配 local 就存本机、
配 oss 就存 OSS,前台都一样走 media.php 代理。

原实现把图标键硬编码成 icons/…(不带存储前缀),导致:
- fb_attach_is_local() 恒为 false
- 本机模式下文件确实写进了 uploads/icons/,
  但 media.php 把它当 OSS 对象去读 → 503,图标完全显示不出来

修法:图标键与附件完全同规则,只是路径里多一段 icons/
  local 模式 → local/icons/YYYY/MM/<hex>.<ext>  落 uploads/icons/
  oss   模式 → <oss.prefix>/icons/YYYY/MM/<hex>.<ext> 落私有桶
前台地址两种模式一致(media.php?k=…),换存储方式旧图标不会失效。

同时:
- fb_icon_is_key/ok 改为同时识别两种前缀
- fb_icon_allowed_ext 移到 attach.php(media.php 与校验都会用,
  不能依赖 admin_taxonomy.php 被加载)
- 写入时加断言:键前缀必须与当前存储模式一致,脱钩就报错,
  不再静默存到错误位置

验证(线上真实环境,两种模式各跑一遍):
- local:键带 local/icons/ 前缀、写入 uploads/icons/ 成功、
  media.php 读回 200 image/png 501B、PNG 魔数正确、删除后 404
- oss:键带 fb/icons/ 前缀、is_local=false、URL 拼接正确
- 旧 bug 形态(icons/… 无前缀)现在被明确拒绝 400,不再误判成 OSS
- 测试后配置已复原为 oss,临时文件与诊断入口全部清理
This commit is contained in:
2026-11-03 08:19:17 +08:00
parent 835c8c05da
commit 872baf4153
3 changed files with 61 additions and 23 deletions
+12 -6
View File
@@ -19,12 +19,8 @@ if (!defined('FB_ROOT')) { http_response_code(403); exit('forbidden'); }
* 对外一律 media.php?k=... 代理,不暴露真实位置。
* ================================================================== */
/** 图标允许的扩展名(比附件白名单窄:图标不需要 gif 动图,也不要 svg) */
function fb_icon_allowed_ext() {
// ⚠️ 刻意不收 svg:SVG 能内嵌 <script>,一旦被当图片直出到浏览器就是 XSS。
// 图标是管理员自己传的,但防线不该依赖「管理员不会传坏东西」。
return ['png', 'jpg', 'jpeg', 'webp'];
}
/** 图标允许的扩展名 —— 定义在 lib/attach.php(fb_icon_allowed_ext),
* 因为 media.php 与 CLI 场景也会用到,不能依赖这个后台文件被加载。 */
/**
* 保存一个上传的图标文件,返回 [bool ok, string 值(键或错误信息), string mime]。
@@ -77,6 +73,16 @@ function fb_icon_store_upload(array $file) {
$key = fb_icon_make_key($ext);
/* 断言:键的前缀必须与当前存储方式一致。
* 这两者一旦脱钩,就会出现「文件写进 uploads/ 但 media.php 去 OSS 找」的 503 ——
* 早期把键硬编码成 icons/… 就是这么坏的。宁可当场报错,也不要静默存错位置。 */
if ($mode === 'local' && !fb_attach_is_local($key)) {
return [false, '内部错误:本地模式下生成了非本机路径的图标键。', ''];
}
if ($mode === 'oss' && fb_attach_is_local($key)) {
return [false, '内部错误:OSS 模式下生成了本机路径的图标键。', ''];
}
if ($mode === 'local') {
if (!fb_local_writable()) { return [false, '本机附件目录不可写(uploads/),无法保存图标。', '']; }
$path = fb_local_path_of($key);