125 lines
3.5 KiB
Go
125 lines
3.5 KiB
Go
|
|
package oss
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"encoding/json"
|
|||
|
|
"io"
|
|||
|
|
"log"
|
|||
|
|
"net/url"
|
|||
|
|
"strings"
|
|||
|
|
|
|||
|
|
"soul-api/internal/database"
|
|||
|
|
"soul-api/internal/model"
|
|||
|
|
|
|||
|
|
alioss "github.com/aliyun/aliyun-oss-go-sdk/oss"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// Config 阿里云 OSS 配置,与管理端 ossConfig 字段对应
|
|||
|
|
type Config struct {
|
|||
|
|
Endpoint string `json:"endpoint"`
|
|||
|
|
Bucket string `json:"bucket"`
|
|||
|
|
Region string `json:"region"`
|
|||
|
|
AccessKeyID string `json:"accessKeyId"`
|
|||
|
|
AccessKeySecret string `json:"accessKeySecret"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// LoadConfig 从 system_config 读取 oss_config,配置不完整时返回 nil
|
|||
|
|
func LoadConfig() *Config {
|
|||
|
|
var row model.SystemConfig
|
|||
|
|
if err := database.DB().Where("config_key = ?", "oss_config").First(&row).Error; err != nil {
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
var m map[string]interface{}
|
|||
|
|
if err := json.Unmarshal(row.ConfigValue, &m); err != nil {
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
var cfg Config
|
|||
|
|
if v, ok := m["endpoint"].(string); ok && v != "" {
|
|||
|
|
cfg.Endpoint = strings.TrimSpace(v)
|
|||
|
|
}
|
|||
|
|
if v, ok := m["bucket"].(string); ok && v != "" {
|
|||
|
|
cfg.Bucket = strings.TrimSpace(v)
|
|||
|
|
}
|
|||
|
|
if v, ok := m["accessKeyId"].(string); ok && v != "" {
|
|||
|
|
cfg.AccessKeyID = v
|
|||
|
|
}
|
|||
|
|
if v, ok := m["accessKeySecret"].(string); ok && v != "" {
|
|||
|
|
cfg.AccessKeySecret = v
|
|||
|
|
}
|
|||
|
|
if cfg.Endpoint == "" || cfg.Bucket == "" || cfg.AccessKeyID == "" || cfg.AccessKeySecret == "" {
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
// endpoint 去掉 scheme,SDK 需要
|
|||
|
|
cfg.Endpoint = strings.TrimPrefix(strings.TrimPrefix(cfg.Endpoint, "https://"), "http://")
|
|||
|
|
return &cfg
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// IsEnabled 是否已配置 OSS 且可用
|
|||
|
|
func IsEnabled() bool {
|
|||
|
|
return LoadConfig() != nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Upload 上传文件到 OSS,objectKey 如 "uploads/avatars/xxx.jpg"
|
|||
|
|
// 返回公网访问 URL,如 https://bucket.oss-cn-hangzhou.aliyuncs.com/uploads/avatars/xxx.jpg
|
|||
|
|
func Upload(objectKey string, reader io.Reader, options ...alioss.Option) (string, error) {
|
|||
|
|
cfg := LoadConfig()
|
|||
|
|
if cfg == nil {
|
|||
|
|
return "", nil // 未配置,调用方需回退本地
|
|||
|
|
}
|
|||
|
|
client, err := alioss.New(cfg.Endpoint, cfg.AccessKeyID, cfg.AccessKeySecret)
|
|||
|
|
if err != nil {
|
|||
|
|
log.Printf("oss: client init failed: %v", err)
|
|||
|
|
return "", err
|
|||
|
|
}
|
|||
|
|
bucket, err := client.Bucket(cfg.Bucket)
|
|||
|
|
if err != nil {
|
|||
|
|
log.Printf("oss: bucket %s failed: %v", cfg.Bucket, err)
|
|||
|
|
return "", err
|
|||
|
|
}
|
|||
|
|
if err := bucket.PutObject(objectKey, reader, options...); err != nil {
|
|||
|
|
log.Printf("oss: PutObject %s failed: %v", objectKey, err)
|
|||
|
|
return "", err
|
|||
|
|
}
|
|||
|
|
// 公网 URL:https://{bucket}.{endpoint}/{objectKey}
|
|||
|
|
u := "https://" + cfg.Bucket + "." + cfg.Endpoint + "/" + objectKey
|
|||
|
|
return u, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Delete 从 OSS 删除对象,objectKey 如 "uploads/avatars/xxx.jpg"
|
|||
|
|
func Delete(objectKey string) error {
|
|||
|
|
cfg := LoadConfig()
|
|||
|
|
if cfg == nil {
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
client, err := alioss.New(cfg.Endpoint, cfg.AccessKeyID, cfg.AccessKeySecret)
|
|||
|
|
if err != nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
bucket, err := client.Bucket(cfg.Bucket)
|
|||
|
|
if err != nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
return bucket.DeleteObject(objectKey)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ParseObjectKeyFromURL 从 OSS 公网 URL 解析出 objectKey
|
|||
|
|
// 格式: https://bucket.oss-cn-xxx.aliyuncs.com/uploads/avatars/xxx.jpg
|
|||
|
|
func ParseObjectKeyFromURL(rawURL string) string {
|
|||
|
|
u, err := url.Parse(rawURL)
|
|||
|
|
if err != nil {
|
|||
|
|
return ""
|
|||
|
|
}
|
|||
|
|
path := strings.TrimPrefix(u.Path, "/")
|
|||
|
|
return path
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// IsOSSURL 判断是否为 OSS 公网 URL(用于删除时区分本地/OSS)
|
|||
|
|
func IsOSSURL(rawURL string) bool {
|
|||
|
|
cfg := LoadConfig()
|
|||
|
|
if cfg == nil {
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
// 格式: https://{bucket}.{endpoint}/...
|
|||
|
|
prefix := "https://" + cfg.Bucket + "." + cfg.Endpoint + "/"
|
|||
|
|
return strings.HasPrefix(rawURL, prefix)
|
|||
|
|
}
|