Files
soul-yongping/soul-api/internal/oss/oss.go

142 lines
3.9 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 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 去掉 schemeSDK 需要
cfg.Endpoint = strings.TrimPrefix(strings.TrimPrefix(cfg.Endpoint, "https://"), "http://")
return &cfg
}
// IsEnabled 是否已配置 OSS 且可用
func IsEnabled() bool {
return LoadConfig() != nil
}
// Upload 上传文件到 OSSobjectKey 如 "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
}
// 公网 URLhttps://{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)
}
// PublicURL 将路径转为 OSS 公网访问 URLpath 如 /uploads/avatars/xxx.jpg
// OSS 未配置时返回空字符串
func PublicURL(path string) string {
cfg := LoadConfig()
if cfg == nil {
return ""
}
path = strings.TrimSpace(path)
if path == "" {
return ""
}
if path[0] == '/' {
path = path[1:]
}
return "https://" + cfg.Bucket + "." + cfg.Endpoint + "/" + path
}