96 lines
4.1 KiB
Go
96 lines
4.1 KiB
Go
// Package imgurl 把数据库里存储的图片存储值拼装为对外可访问的完整 URL。
|
||
//
|
||
// 存储约定(见迁移 007):库里只存「对象 key」(如 runway/abc.jpg)或「本地相对路径」
|
||
// (如 /uploads/runway/x.jpg),绝不存完整 URL。这样做两个好处:
|
||
// 1. 换 CDN 域名只改配置,无需迁移全表;
|
||
// 2. 做「普通 / 高清」分级时无需从 URL 反向解析 key。
|
||
//
|
||
// 质量模型(2026-09-11 调整):VIP 与免费用户现在看到同一质量,高清样式名为 `high`,
|
||
// 由缤纷云 CoreIX 公开样式提供(base_url/key!style:high),匿名可访问、免 token。
|
||
// 旧「免费低清 / VIP 原图预签名」付费墙已取消,故 Composer 不再区分 tier。
|
||
// - 列表缩略图:另走 `thumb` 公开样式(ComposeThumb),仅用于列表/卡片等小尺寸场景,省流量。
|
||
// - 本地兜底 /uploads/*:原样返回(前端 toAbs 拼 host)。
|
||
// - 外链 http(s):原样返回(历史遗留 / vogue 等,无法套样式)。
|
||
package imgurl
|
||
|
||
import (
|
||
"fmt"
|
||
"strings"
|
||
)
|
||
|
||
// Composer 图片 URL 拼装器。
|
||
type Composer struct {
|
||
// BaseURL 对外访问域名,如 https://toomstudio.s3.bitiful.net(末尾无斜杠)。
|
||
BaseURL string
|
||
// StyleDisplay 全量展示图 CoreIX 样式名(如 "high",对应控制台建的公开样式);
|
||
// 若值含 "=" 则视为查询串(如 "w=1080&q=80&fmt=webp"),拼成 base_url/key?查询串。
|
||
StyleDisplay string
|
||
// StyleThumb 列表缩略图 CoreIX 样式名(如 "thumb");仅用于列表/卡片等小尺寸场景。
|
||
// 留空时回落 StyleDisplay(无独立缩略图样式时行为与旧版一致)。
|
||
StyleThumb string
|
||
}
|
||
|
||
// New 构造 Composer。
|
||
// - styleDisplay 缺省回落 "high"(高清公开样式,VIP 与免费用户同质量);
|
||
// - styleThumb 缺省回落 styleDisplay(未单独配置缩略图样式时,列表与详情同图)。
|
||
func New(baseURL, styleDisplay, styleThumb string) *Composer {
|
||
if styleDisplay == "" {
|
||
styleDisplay = "high"
|
||
}
|
||
if styleThumb == "" {
|
||
styleThumb = styleDisplay
|
||
}
|
||
return &Composer{
|
||
BaseURL: strings.TrimRight(baseURL, "/"),
|
||
StyleDisplay: styleDisplay,
|
||
StyleThumb: styleThumb,
|
||
}
|
||
}
|
||
|
||
// Compose 把存储值拼成可访问 URL(详情/大图场景,统一走 StyleDisplay)。
|
||
//
|
||
// - stored 为空:返回 ""
|
||
// - 本地兜底 /uploads/*:原样返回(前端 toAbs 拼 host)
|
||
// - 已是完整 http(s) URL(历史遗留 / 外链图):原样返回(无法套样式)
|
||
// - key:base_url/key!style:high(或查询串模式)
|
||
func (c *Composer) Compose(stored string) string {
|
||
return c.compose(stored, c.StyleDisplay)
|
||
}
|
||
|
||
// ComposeThumb 把存储值拼成「列表缩略图」URL(卡片 / 列表封面 / 图条等小尺寸场景)。
|
||
// 与 Compose 共用同一套兜底逻辑,仅样式名换成 StyleThumb(未配置时回落 StyleDisplay)。
|
||
// 注意:仅适用于同一 base_url 下的对象 key;外链与 /uploads 路径不做处理(无法套样式)。
|
||
func (c *Composer) ComposeThumb(stored string) string {
|
||
return c.compose(stored, c.StyleThumb)
|
||
}
|
||
|
||
// compose 应用指定样式拼装 URL。
|
||
func (c *Composer) compose(stored, style string) string {
|
||
stored = strings.TrimSpace(stored)
|
||
if stored == "" {
|
||
return ""
|
||
}
|
||
// 本地兜底路径:由前端拼绝对地址,原样返回。
|
||
if strings.HasPrefix(stored, "/uploads/") {
|
||
return stored
|
||
}
|
||
// 已是完整 URL(历史遗留 / 外链图):原样返回(无法套样式)。
|
||
if strings.HasPrefix(stored, "http://") || strings.HasPrefix(stored, "https://") {
|
||
return stored
|
||
}
|
||
// 对象 key:按指定样式拼装(命名样式 key!style:xxx 或查询串 key?w=..&q=..)。
|
||
return c.styledURL(stored, style)
|
||
}
|
||
|
||
// styledURL 按样式名生成地址:命名样式(key!style:high)或查询串(key?w=..&q=..)两种模式。
|
||
func (c *Composer) styledURL(key, style string) string {
|
||
if strings.Contains(style, "=") {
|
||
sep := "?"
|
||
if strings.Contains(style, "?") {
|
||
sep = "&"
|
||
}
|
||
return fmt.Sprintf("%s/%s%s%s", c.BaseURL, key, sep, style)
|
||
}
|
||
return fmt.Sprintf("%s/%s!style:%s", c.BaseURL, key, style)
|
||
}
|