桌宠素材外置实践:用 OBS + CDN 托管 31 张透明 PNG
一、为什么要外置
桌宠的立绘是整帧 PNG,单张 200~600 KB,31 张加上动画帧很容易把安装包推到 16 MB 以上。更麻烦的是素材迭代:换一套表情就要重新打包、签名、发版,用户还得再下一次整包。把素材放到对象存储、客户端按需下载,包体可以压到 3 MB 左右,换图只改远端目录。
二、桶与目录规划
不要把所有图平铺在一个目录里。按版本切前缀,客户端只认一个 manifest,回滚时把 manifest 指回旧版本即可:
pet-assets/
manifest.json
v3/frame-neutral.png
v3/frame-blink.png
v3/frame-sleep.png
manifest 里记录版本号、每张图的 SHA-256 和文件大小,客户端先拉它,再决定哪些帧需要更新。
三、上传脚本
用 OBS 的 Python SDK 并发上传,同时把 Content-Type 和 Cache-Control 写对。这两项如果交给控制台默认值,CDN 边缘经常缓存不住:
import os
from obs import ObsClient
client = ObsClient(
access_key_id=AK,
secret_access_key=SK,
server="https://obs.cn-north-4.myhuaweicloud.com",
)
for name in os.listdir("frames"):
path = os.path.join("frames", name)
client.putFile(
"pet-assets",
f"v3/{name}",
path,
headers={
"Content-Type": "image/png",
"Cache-Control": "public, max-age=31536000, immutable",
},
)
四、缓存策略
版本化路径天然适合长缓存:v3/ 下面的文件内容不会变,直接给一年 immutable;manifest.json 相反,必须 no-cache,否则客户端永远看不到新版本。CDN 侧只对 /v*/* 做缓存,manifest 回源。
五、客户端按需加载
Android 侧用 OkHttp 拉取,落盘前校验哈希,避免半截文件导致花屏:
val manifest = api.get("$base/manifest.json")
val file = File(cacheDir, "frame-$name.png")
if (!file.exists() || sha256(file) != manifest.hashes[name]) {
val body = okhttp.newCall(
Request.Builder().url("$base/${manifest.version}/frame-$name.png").build()
).execute().body!!
body.byteStream().use { input ->
file.outputStream().use { out -> input.copyTo(out) }
}
}
首次启动只下待机、眨眼、点击三帧,其余在空闲时预取;弱网下先用包内自带的兜底图,不要让桌宠开天窗。
六、成本与踩坑
透明 PNG 不要用有损量化压,alpha 边缘会出现灰边,改用 oxipng 做无损压缩,通常还能再小 15%。CDN 回源带宽按量计费,发布前先预热 v3/ 目录;另外记得给桶配防盗链或签名 URL,素材虽然不敏感,但被别的站直接引用会白烧流量。
七、小结
外置素材带来的收益不只是包体:素材热更新、A/B 两套立绘、按设备分辨率下发不同尺寸都变得简单。代价是要多维护一份 manifest 和一套校验逻辑,但对桌宠这种高频迭代的产品,这笔账是划算的。
- 点赞
- 收藏
- 关注作者
评论(0)