Files
backend_v2/internal/pkg/storage/storage.go
toom1996 10d8a96e8c update
2026-09-07 00:04:01 +08:00

72 lines
2.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package storage 抽象「把一段图片字节存成可访问地址」的实现。
//
// 两种实现:
// - LocalUploader:落到本地磁盘,对外以静态路径暴露(兜底/默认实现)。
// - QiniuUploader:直传七牛云对象存储(标准库实现,无第三方 SDK 依赖)。
//
// 入库 worker 主用 Qiniu(若启用),单张失败时兜底写本地,避免图片完全丢失。
package storage
import (
"context"
"os"
"path/filepath"
"strings"
)
// Uploader 抽象图片上传。
// Upload 返回写入数据库的图片访问地址:
// - Qiniu 返回完整 https URL(base_url + key)
// - 本地实现返回 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 抽象图片删除(清理孤儿文件用)。
// QiniuUploader / 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
}