状态:已构建,尚未部署 / 未接入 kira-cdn。 该 API 已在代码中实现,但服务尚未上线,调用方(kira-cdn)也还未改造。当前线上变体路径仍是 imgproxy。详见 kira-image 概述。
kira-image 是内部专用服务,无公网入口。所有调用走 Fly 6PN 私网 kira-image.internal:8083,并以共享密钥 X-Internal-Key 鉴权(kira-cdn → kira-image)。共两个端点:
| 方法 | 路径 | 鉴权 | 说明 |
|---|
GET | /health | 无 | 健康检查 |
POST | /v1/ensure | X-Internal-Key | 确保变体存在并返回 URL |
GET /health
curl http://kira-image.internal:8083/health
# → ok
返回纯文本 ok,供 Fly 做实例存活探测。
POST /v1/ensure
给定一个源图和一组变体规格,对每个规格:
- 由
(源标识 ‖ 规范化变换 ‖ 格式) 算出确定性变体 key;
- HEAD R2 检查是否已存在;
- 若任一变体缺失,则仅抓取一次源图,对缺失项生成并以公开对象写入 R2;
- 返回每个规格名对应的公开
cdn.kira.art URL。
请求头必须携带与服务端 KIRA_IMAGE_INTERNAL_KEY 完全相同的值:
X-Internal-Key: $KIRA_IMAGE_INTERNAL_KEY
缺失或不匹配返回 401 Unauthorized。
请求体
{
// 二选一:从 R2 直接按 key 读,或从外部 URL 抓取
"source": { "key": "u/U/thread/T/asset.png" }, // 或 { "url": "https://…" }
"specs": [
{
"name": "thumb", // 必填:本变体在响应里的标识
"width": 400, // 默认 0 = 该轴不约束
"height": 0, // 默认 0 = 该轴不约束
"quality": 80, // 可选;JPEG 默认 80 / WebP 默认 75
"format": "webp", // 可选;webp|jpeg|jpg|png,默认 webp(avif 会降级为 webp)
"resizingType": "fit", // 可选;fit(默认)| fill | auto
"enlarge": false, // 可选;默认 false(不放大小图)
"blur": null, // 可选;高斯模糊 sigma(> 0 生效)
"maxBytes": null, // 可选;设置后对质量做二分搜索以装入预算
"resampling": "lanczos3" // 可选;lanczos3(默认)| lanczos2 | cubic | linear | nearest
}
]
}
字段名是 camelCase(resizingType、maxBytes),由 serde rename_all = "camelCase" 决定。source 必须含 key 或 url 之一,否则返回 400;两者都缺时无法定位源图。
spec 字段
| 字段 | 类型 | 默认 | 说明 |
|---|
name | string | (必填) | 响应 variants map 的 key |
width | uint | 0 | 目标宽度;0 = 该轴不约束 |
height | uint | 0 | 目标高度;0 = 该轴不约束 |
quality | uint8? | JPEG 80 / WebP 75 | 编码质量;未提供时用服务端默认 |
format | string? | webp | webp / jpeg / jpg / png;公开 URL 是单一烘焙格式,无 Accept 协商 |
resizingType | string? | fit | fit=装入框、fill=覆盖后居中裁剪、auto=keying 区分但缩放同 fit |
enlarge | bool | false | 是否允许放大小于目标框的源图 |
blur | float? | null | 高斯模糊 sigma,> 0 才应用 |
maxBytes | uint? | null | 输出字节预算;设置后在 [10, quality] 内二分搜索最高可用质量 |
resampling | string? | lanczos3 | 重采样滤波器;计入变体 key(见概述备注) |
200 OK,body 是 { "variants": { 规格名: 公开URL } }:
{
"variants": {
"thumb": "https://cdn.kira.art/img/ab/cd/<blake3_hash>.webp",
"preview": "https://cdn.kira.art/img/ef/01/<blake3_hash>.webp"
}
}
curl -X POST http://kira-image.internal:8083/v1/ensure \
-H "Content-Type: application/json" \
-H "X-Internal-Key: $KIRA_IMAGE_INTERNAL_KEY" \
-d '{
"source": { "key": "u/U/thread/T/asset.png" },
"specs": [
{ "name": "thumb", "width": 400, "quality": 80, "format": "webp" },
{ "name": "preview", "width": 1024, "quality": 85, "format": "webp" }
]
}'
// 200 OK
{
"variants": {
"thumb": "https://cdn.kira.art/img/3a/9f/3a9f….webp",
"preview": "https://cdn.kira.art/img/7c/12/7c12….webp"
}
}
幂等性
变体 key 是 (源标识 ‖ 规范化变换 ‖ 格式) 的 keyed-hash,因此相同源 + 相同规格 ⇒ 相同 key ⇒ 同一个 R2 对象。重复 ensure 同一组变体时:
- 命中(对象已存在):只做一次 R2 HEAD,根本不抓取源图、不重新编码,直接返回 URL。
- 部分缺失:只抓取一次源图,仅对缺失项生成。
这正是它能在稳态下极致缩容的原因——变体一旦存在,后续请求几乎零成本,且公开 URL 永不变化(不会冲掉 Cloudflare 缓存)。
错误码
| 状态码 | 触发条件 |
|---|
400 Bad Request | source 既无 key 也无 url |
401 Unauthorized | X-Internal-Key 缺失或不匹配 |
422 Unprocessable Entity | 图片处理失败(如源图超过 KIRA_IMAGE_MAX_SRC_PIXELS、解码失败) |
502 Bad Gateway | 源图抓取失败,或 R2 写入失败 |
503 Service Unavailable | 并发信号量获取失败 |
504 Gateway Timeout | 单图处理超过 KIRA_IMAGE_PROCESS_TIMEOUT_SECS(默认 30s) |
当请求 avif 时,输出会降级为 WebP,服务用真实格式重算 store key,返回的 URL 因此以 .webp 结尾、指向真正写入的字节。