主题素材可放任意 S3 兼容存储,生成器内测通后一键上传
素材位置改成两选一(本地随二进制 / 自行填 Endpoint 的 S3 兼容存储), R2、MinIO、B2、OSS-COS 的 S3 端点都只差 Endpoint/Region/路径风格。 - serve.py:手写 SigV4(PUT/GET/DELETE/HEAD),新增 /api/s3/test 与 /api/s3/upload。测试连接依次验连通、写权限、读回一致、匿名可读, 失败把 S3 的 <Code> 翻成可执行的建议;上传带 immutable 缓存头。 - index.html:新增「素材托管」面板(模式切换 + 7 个字段 + 测试/上传按钮), 上传集合与导出共用 usedAssetIds();成功后自动回填「素材前缀」。 密钥只进 sessionStorage 与内存,不进草稿/工程 json/导出 zip。 - 修:usedAssetIds 返回 Set,误用 length/map 导致「还没有素材」。 - 修:字段填完 700ms 内刷新会丢配置 → 失焦与模式切换时立即落盘。 - 自检:check_sigv4.py 与 aws-sdk-go-v2 signer 逐字节对拍(6/6), selftest_upload.py 起假 S3 跑通测试连接→上传→读回(16 项), mock_s3.py 供手动点面板用。
This commit is contained in:
@@ -190,13 +190,61 @@ WebP 体积 ≈ 像素数 × 质量。**真正的大头是像素**:CSS 里只
|
||||
| --- | --- | --- |
|
||||
| 质量 | 0.88 | 0.85~0.9 肉眼几乎看不出差别,低于 0.8 才明显糊 |
|
||||
| 长边上限 | 1600 | 只缩不放。整页背景建议 1920/不限制;装饰人物 1200 够用 |
|
||||
| 素材前缀 | 空 | 空 = 图片放 `static/images/themes/<slug>/` 随二进制发布;填 `https://cdn.example.com/themes` = CSS 写绝对地址,图片自己传对象存储(R2/OSS)。**前缀里不要带 slug** |
|
||||
| 素材前缀 | 空 | 空 = 图片放 `static/images/themes/<slug>/` 随二进制发布;填 `https://cdn.example.com/themes` = CSS 写绝对地址,图片放自己的对象存储(R2 / MinIO / B2 / OSS / COS,任何 S3 兼容都行)。**前缀里不要带 slug** |
|
||||
|
||||
导出的 CSS 只有 `urlOf()` 一处决定图片地址,图层、背景、窄屏块全走它,不会出现
|
||||
「背景走 CDN、装饰还指本地」的半吊子状态。素材前缀只影响导出,预览永远是内联 dataURL。
|
||||
|
||||
详细取舍(生成器前缀 / 应用内 301 / 边缘改写)见 `docs/theme-assets-cdn.md`。
|
||||
|
||||
## 素材托管(本地 / 任意 S3 兼容)
|
||||
|
||||
「图片放哪」由你自己定,面板里二选一:
|
||||
|
||||
- **本地**(默认):打进 `static/images/themes/<slug>/`,随二进制发布。部署即生效、不依赖外部服务,
|
||||
代价是换一张图要重新编译。
|
||||
- **S3 兼容存储**:传到你自己的对象存储,导出的 CSS 写绝对地址。这里刻意不锁厂商 ——
|
||||
R2 / MinIO / Backblaze B2 / 阿里 OSS(S3) / 腾讯 COS(S3) 用的是同一套 SigV4,
|
||||
差别只在 `Endpoint`、`Region`、是否路径风格,全都做成了字段。
|
||||
|
||||
填好 Endpoint / Region / 桶名 / 路径前缀 / 密钥后:
|
||||
|
||||
1. **测试连接** —— 依次验四件事:`HEAD` 桶(连通与凭据)、`PUT` 一个探针对象(写权限)、
|
||||
`GET` 读回比对(内容一致)、匿名 `GET`(传上去的图能不能被浏览器直接读到),最后删掉探针。
|
||||
失败时会把 S3 的 `<Code>` 翻成人话(`SignatureDoesNotMatch` → 密钥/Region/时钟三选一)。
|
||||
2. **上传素材** —— 把本次主题真正引用到的图(与导出同一个集合,`usedAssetIds()`)逐张 PUT,
|
||||
键为 `<路径前缀>/<slug>/<id>.webp`,带 `Cache-Control: public, max-age=31536000, immutable`。
|
||||
成功后自动把上面的「素材前缀」填成推导出的公开地址。
|
||||
|
||||
⚠️ 上传走的是本机 `serve.py` 的 `/api/s3/test`、`/api/s3/upload`,浏览器不直连对象存储 ——
|
||||
所以**不用给桶配 CORS**,密钥也不会出现在页面发往第三方的请求里。它只存在当前标签页的
|
||||
`sessionStorage` 与内存:不进 `localStorage` 草稿、不进工程 json、不进导出 zip。
|
||||
用 `python -m http.server` 起的只有静态服务,探测不到接口时按钮会禁用并说明原因。
|
||||
|
||||
公开地址留空时按 endpoint 推导(path 风格 = `<endpoint>/<桶>/<前缀>`)。R2 与私有桶
|
||||
需要自定义域或 CDN 域名,那种情况自己填「公开前缀」。测试里的「匿名可读」会给一条提醒,
|
||||
但不会拦上传 —— 很多人就是靠自定义域分发的。
|
||||
|
||||
厂商怎么填(2026 年的常见值,各家控制台都有「S3 API」页面):
|
||||
|
||||
| 厂商 | Endpoint | Region | 路径风格 |
|
||||
| --- | --- | --- | --- |
|
||||
| Cloudflare R2 | `https://<账号ID>.r2.cloudflarestorage.com` | `auto` | 勾选 |
|
||||
| MinIO(自建) | `http://<host>:9000` | `us-east-1`(看服务端配置) | 勾选 |
|
||||
| Backblaze B2 | `https://s3.<区域>.backblazeb2.com` | 区域名,如 `us-west-004` | 勾选 |
|
||||
| 阿里云 OSS | `https://s3.oss-<区域>.aliyuncs.com` | 区域,如 `cn-hangzhou` | 看控制台 |
|
||||
| 腾讯云 COS | `https://cos.<区域>.myqcloud.com` | 区域,如 `ap-hangzhou` | 看控制台 |
|
||||
|
||||
AWS S3 或已绑定的桶级自定义域可以取消「路径风格」(走 `<桶>.<endpoint>`)。
|
||||
|
||||
两条自检(都在本目录,不依赖 UI):
|
||||
|
||||
```bash
|
||||
python dev/theme-builder/check_sigv4.py # 与 aws-sdk-go-v2 的 signer 逐字节对拍
|
||||
python dev/theme-builder/selftest_upload.py # 起假 S3,跑「测试连接 → 上传 → 读回」
|
||||
python dev/theme-builder/mock_s3.py --port 9900 # 想手动点面板时,先起这个假 S3
|
||||
```
|
||||
|
||||
面板底部实时显示 `3 张,共 19KB;最大一张 bg.webp 7KB(1920×1080)`。
|
||||
改完立刻按「原图 + fx 参数」重编码全部素材,并报出省了多少 —— 与图片处理同一条非破坏原则,
|
||||
来回试不会叠加压缩。
|
||||
|
||||
Reference in New Issue
Block a user