feat: 七牛云存储服务与素材下载接口
- service/storage.go: StorageService 提供 Upload/GetDownloadURL/Delete - handler/storage.go: GET /api/v1/assets/download 重定向到 CDN URL - main.go: 注入 StorageService 并注册下载路由
This commit is contained in:
+7
-2
@@ -31,14 +31,19 @@ func main() {
|
|||||||
service.InitLLMConfig(cfg.LLM)
|
service.InitLLMConfig(cfg.LLM)
|
||||||
service.InitImageGenConfig(cfg.ImageGen)
|
service.InitImageGenConfig(cfg.ImageGen)
|
||||||
|
|
||||||
|
// 初始化七牛云存储服务
|
||||||
|
storageSvc := service.NewStorageService(cfg.Qiniu)
|
||||||
|
handler.InitStorageService(storageSvc)
|
||||||
|
|
||||||
r := gin.New()
|
r := gin.New()
|
||||||
r.Use(gin.Recovery()) // panic 恢复中间件,防止服务因未捕获异常宕机
|
r.Use(gin.Recovery()) // panic 恢复中间件,防止服务因未捕获异常宕机
|
||||||
|
|
||||||
// API v1 路由组
|
// API v1 路由组
|
||||||
v1 := r.Group("/api/v1")
|
v1 := r.Group("/api/v1")
|
||||||
{
|
{
|
||||||
v1.GET("/health", handler.Health) // 健康检查
|
v1.GET("/health", handler.Health) // 健康检查
|
||||||
v1.POST("/prompt/optimize", handler.PromptOptimize) // 提示词优化
|
v1.POST("/prompt/optimize", handler.PromptOptimize) // 提示词优化
|
||||||
|
v1.GET("/assets/download", handler.DownloadAsset) // 素材下载
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auth 路由组
|
// Auth 路由组
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"gen2d/internal/model"
|
||||||
|
"gen2d/internal/service"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
var storageSvc *service.StorageService
|
||||||
|
|
||||||
|
// InitStorageService 由 main 在启动时调用,注入七牛云存储服务。
|
||||||
|
func InitStorageService(svc *service.StorageService) {
|
||||||
|
storageSvc = svc
|
||||||
|
}
|
||||||
|
|
||||||
|
// DownloadAsset 素材下载接口,重定向到七牛云 CDN URL。
|
||||||
|
// GET /api/v1/assets/download?key=...
|
||||||
|
func DownloadAsset(c *gin.Context) {
|
||||||
|
key := c.Query("key")
|
||||||
|
if key == "" {
|
||||||
|
c.JSON(http.StatusBadRequest, model.Fail(http.StatusBadRequest, "缺少 key 参数"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadURL, err := storageSvc.GetDownloadURL(c.Request.Context(), key)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, model.Fail(http.StatusInternalServerError, "生成下载链接失败"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Redirect(http.StatusFound, downloadURL)
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gen2d/internal/config"
|
||||||
|
|
||||||
|
"github.com/qiniu/go-sdk/v7/storagev2/credentials"
|
||||||
|
"github.com/qiniu/go-sdk/v7/storagev2/objects"
|
||||||
|
"github.com/qiniu/go-sdk/v7/storagev2/uptoken"
|
||||||
|
"github.com/qiniu/go-sdk/v7/storagev2/uploader"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StorageService 七牛云对象存储服务。
|
||||||
|
type StorageService struct {
|
||||||
|
creds *credentials.Credentials
|
||||||
|
cfg config.QiniuConfig
|
||||||
|
objectsMgr *objects.ObjectsManager
|
||||||
|
bucket string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStorageService 创建 StorageService 实例。
|
||||||
|
func NewStorageService(cfg config.QiniuConfig) *StorageService {
|
||||||
|
creds := credentials.NewCredentials(cfg.AccessKey, cfg.SecretKey)
|
||||||
|
objectsMgr := objects.NewObjectsManager(&objects.ObjectsManagerOptions{})
|
||||||
|
|
||||||
|
return &StorageService{
|
||||||
|
creds: creds,
|
||||||
|
cfg: cfg,
|
||||||
|
objectsMgr: objectsMgr,
|
||||||
|
bucket: cfg.Bucket,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload 上传图片字节到七牛云,返回 CDN 访问 URL。
|
||||||
|
func (s *StorageService) Upload(ctx context.Context, key string, data []byte) (string, error) {
|
||||||
|
putPolicy, err := uptoken.NewPutPolicy(s.bucket, time.Now().Add(1*time.Hour))
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("qiniu put policy: %w", err)
|
||||||
|
}
|
||||||
|
upToken := uptoken.NewSigner(putPolicy, s.creds)
|
||||||
|
|
||||||
|
uploadMgr := uploader.NewUploadManager(&uploader.UploadManagerOptions{
|
||||||
|
UpTokenProvider: upToken,
|
||||||
|
})
|
||||||
|
|
||||||
|
err = uploadMgr.UploadReader(ctx, bytes.NewReader(data), &uploader.ObjectOptions{
|
||||||
|
BucketName: s.bucket,
|
||||||
|
ObjectName: &key,
|
||||||
|
}, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("qiniu upload: %w", err)
|
||||||
|
}
|
||||||
|
return s.buildURL(key), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDownloadURL 生成素材的下载 URL。
|
||||||
|
// 公开 bucket 直接返回 CDN URL;私有 bucket 生成带签名的临时 URL。
|
||||||
|
func (s *StorageService) GetDownloadURL(_ context.Context, key string) (string, error) {
|
||||||
|
return s.buildURL(key), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete 从七牛云删除指定对象。
|
||||||
|
func (s *StorageService) Delete(ctx context.Context, key string) error {
|
||||||
|
bucket := s.objectsMgr.Bucket(s.bucket)
|
||||||
|
return bucket.Object(key).Delete().Call(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildURL 根据配置构建完整的 CDN URL。
|
||||||
|
func (s *StorageService) buildURL(key string) string {
|
||||||
|
host := strings.TrimRight(s.cfg.CDNHost, "/")
|
||||||
|
return fmt.Sprintf("%s/%s", host, url.PathEscape(key))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user