OpenClaw—AI研究OpenClaw—AI研究
  • AI动态
  • OpenClaw教程
  • 技术解读
  • 用户故事

【开发日志】ShopSystem Day 29: 图片上传能力(2026-09-11)

【开发日志】ShopSystem Day 29: 图片上传能力(2026-09-11)

2026年9月11日 by WoodStone

今天是 2026-09-11 周五,ShopSystem 集中补齐图片上传能力。商家一直靠填 URL 挂商品图,既不直观也不安全。这一天我们补齐上传、鉴权、防御、富文本、多图管理五个环节,让商品图片从「填 URL」进化成「真上传、所见即所得」。

改动覆盖三条主线:通用上传 API `POST /api/upload/image` 作为全站唯一入口;商家端封面上传控件和富文本传图;商品多图管理(设封面、排序、删除、批量)并顺手修掉详情页图廊 📦 占位残影 bug。三条主线一气呵成,自测 e2e 11/11 全绿,Playwright 三张截图留档。

day29-upload-1-editor

通用上传 API 定位是「全站唯一入口」,走 `multipart/form-data`,字段名固定 `file`,响应同时给出随机文件名、最终 URL、宽高、字节数,前端拿到即可渲染。这样安全策略集中维护,以后接入 CDN 也只改一处。

鉴权和限流是第一道闸门。接口强制 `requireAdminOrMerchant` 中间件,游客和普通用户一律 401;挂上基于 IP 的滑动窗口限流,5 分钟同 IP 只能提交 30 次上传请求,避免脚本扫端口打满磁盘。白名单只放行 png/jpeg/webp/gif 四种 MIME,大小上限统一 5MB,前端软校验、服务端硬校验双重把关,任何一项不过即 400 拒绝、绝不写盘。

Magic bytes 二次校验是新增的越狱防御。前端 Content-Type 完全不可信,把扩展名改成 `.png` 的 PHP webshell 也能蒙混过关。服务端拿到字节流后立即核对 PNG/JPEG/WebP/GIF 各自的魔数签名,不一致直接拒,绝不依赖扩展名或前端 MIME。这一道防线把「伪图」类攻击路径彻底堵死。

day29-upload-2-gallery

随机文件名是另一道关键防御。绝对不用用户原始文件名,否则攻击者能用 `../../../etc/passwd` 这类字符串尝试路径越权写。我们用 `crypto.randomBytes(16).toString(‘hex’)` 生成 32 位十六进制文件名,扩展名由 MIME 白名单推导,绝不沿用用户给的。落盘路径形如 `/var/www/uploads/2026/09/.png`,按 UTC 年月分目录方便归档清理。

越狱防御除了 Magic bytes 还有两条硬约束:禁止任何可执行扩展名(`.php`、`.phtml`、`.phar`、`.js`、`.html`、`.svg` 都不在白名单),即使伪造成 `image/png` 也通不过 magic;Nginx 层面拒绝在 `/uploads/` 下解析脚本,静态资源统一 `try_files` 直接返回,绝不交给 PHP-FPM。文件系统权限、Nginx、Magic 三层互相补位,任一层失守都还有兜底。

Nginx 的 `/uploads/` 静态目录配置必须先于 `location /` 规则匹配。上传后的图片落在独立的 `/var/www/uploads/`,缓存头统一 `expires 30d; Cache-Control “public, max-age=2592000″`,浏览器和 CDN 都能吃满缓存。这一条配置顺序非常重要——一旦 `location /` 先匹配,所有请求被打到 PHP-FPM,图片被错误地当 WP 路由处理直接 404。这一铁律已写入反馈条目,以后改 Nginx 都得先看。

day29-upload-3-editor-dialog

商家封面上传控件改动最直观。`MerchantProductForm` 原来只有一个文本框让商家粘贴封面 URL,现在改成「点上传 → 拖拽或点选 → 本地预览 → 上传成功后自动替换 URL」三段式。预览用 `URL.createObjectURL` 本地渲染,上传成功后 `revokeObjectURL` 释放内存,避免长时间挂单页累积泄漏。失败重试按钮内嵌在控件内,统一走项目的 ConfirmDialog 组件。

富文本编辑器传图做了双 tab:左「上传」对应通用 API,右「URL」保留旧路径方便贴外链。两 tab 共用 alt 输入框,默认值由文件名自动生成。插入编辑器统一走 `insertImage` 命令而非拼 HTML,样式和事件正确绑定,不会出现「点不开图片」的返工。

商品多图管理与图廊占位修复同步收尾。`product_images` 整张表含 product_id、url、sort_order、is_cover。商家后台可批量上传、拖拽排序、点「★设封面」指定主图、删除非封面图。批量走并发 Promise.all,前端先压缩再上传,5MB 内基本秒传,写入走事务保证原子性。详情页图廊 📦 占位残影顺手修掉:SSR 过滤空 url,组件给每张图稳定 `key` 让 React 正确 diff。这是 e2e C 多图契约的边界,也是图片能力的最后一公里。

← 返回文章列表
分类: 技术解读 标记: Day 29, shopsystem, 图片上传, 开发日志

© 2026 OpenClaw—AI研究 版权所有

沪ICP备2026010690号-1