72 lines
2.7 KiB
Go
72 lines
2.7 KiB
Go
// Package storage 抽象「把一段图片字节存成可访问地址」的实现。
|
||
//
|
||
// 两种实现:
|
||
// - LocalUploader:落到本地磁盘,对外以静态路径暴露(兜底/默认实现)。
|
||
// - S4Uploader:直传缤纷云 S4 对象存储(兼容 S3,基于 aws-sdk-go-v2)。
|
||
//
|
||
// 入库 worker 主用 S4(若启用),单张失败时兜底写本地,避免图片完全丢失。
|
||
package storage
|
||
|
||
import (
|
||
"context"
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
)
|
||
|
||
// Uploader 抽象图片上传。
|
||
// Upload 返回写入数据库的图片访问地址:
|
||
// - S4 返回对象 key(base_url 由 imgurl.Composer 在渲染时拼)
|
||
// - 本地实现返回 Upload.URLPrefix + "/" + key 的相对路径
|
||
type Uploader interface {
|
||
// Upload 上传 data 到 key 指定的对象,返回访问 URL。
|
||
Upload(ctx context.Context, key string, data []byte, contentType string) (string, error)
|
||
// Enabled 表示该实现是否真正启用(用于日志/兜底判断)。
|
||
Enabled() bool
|
||
}
|
||
|
||
// Deleter 抽象图片删除(清理孤儿文件用)。
|
||
// S4Uploader / LocalUploader 都实现本接口;仅当某 key 在所有图集/草稿表中
|
||
// 引用计数归零时才由调用方触发,避免误删仍被引用的文件。
|
||
type Deleter interface {
|
||
// Delete 删除 key 指定的对象/文件。key 不存在时按成功处理(幂等)。
|
||
Delete(ctx context.Context, key string) error
|
||
}
|
||
|
||
// LocalUploader 把文件落到本地磁盘并对外以静态路径暴露(兜底/默认实现)。
|
||
type LocalUploader struct {
|
||
Dir string
|
||
URLPrefix string
|
||
}
|
||
|
||
// NewLocalUploader 构造本地上传器。
|
||
func NewLocalUploader(dir, urlPrefix string) *LocalUploader {
|
||
return &LocalUploader{Dir: dir, URLPrefix: strings.TrimRight(urlPrefix, "/")}
|
||
}
|
||
|
||
// Enabled 本地上传始终可用。
|
||
func (l *LocalUploader) Enabled() bool { return true }
|
||
|
||
// Upload 写本地文件并返回相对静态路径(如 /uploads/runway/xxx.jpg)。
|
||
func (l *LocalUploader) Upload(ctx context.Context, key string, data []byte, contentType string) (string, error) {
|
||
abs := filepath.Join(l.Dir, filepath.FromSlash(key))
|
||
if err := os.MkdirAll(filepath.Dir(abs), 0o755); err != nil {
|
||
return "", err
|
||
}
|
||
if err := os.WriteFile(abs, data, 0o644); err != nil {
|
||
return "", err
|
||
}
|
||
return l.URLPrefix + "/" + strings.TrimLeft(key, "/"), nil
|
||
}
|
||
|
||
// Delete 删除本地文件(清理孤儿图用)。key 可能带 URLPrefix 前缀,先剥掉再定位磁盘文件。
|
||
func (l *LocalUploader) Delete(ctx context.Context, key string) error {
|
||
rel := strings.TrimPrefix(key, l.URLPrefix)
|
||
rel = strings.TrimPrefix(rel, "/")
|
||
abs := filepath.Join(l.Dir, filepath.FromSlash(rel))
|
||
if err := os.Remove(abs); err != nil && !os.IsNotExist(err) {
|
||
return err
|
||
}
|
||
return nil
|
||
}
|