115 lines
2.8 KiB
Go
115 lines
2.8 KiB
Go
package handler
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"healthflow/internal/config"
|
|
|
|
"github.com/aws/aws-sdk-go-v2/aws"
|
|
awsconfig "github.com/aws/aws-sdk-go-v2/config"
|
|
"github.com/aws/aws-sdk-go-v2/credentials"
|
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
type UploadHandler struct {
|
|
cfg *config.Config
|
|
s3Client *s3.Client
|
|
}
|
|
|
|
func NewUploadHandler(cfg *config.Config) *UploadHandler {
|
|
h := &UploadHandler{cfg: cfg}
|
|
|
|
// 初始化 R2 客户端
|
|
if cfg.R2AccountID != "" && cfg.R2AccessKeyID != "" {
|
|
r2Resolver := aws.EndpointResolverWithOptionsFunc(func(service, region string, options ...interface{}) (aws.Endpoint, error) {
|
|
return aws.Endpoint{
|
|
URL: fmt.Sprintf("https://%s.r2.cloudflarestorage.com", cfg.R2AccountID),
|
|
}, nil
|
|
})
|
|
|
|
awsCfg, err := awsconfig.LoadDefaultConfig(context.TODO(),
|
|
awsconfig.WithEndpointResolverWithOptions(r2Resolver),
|
|
awsconfig.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(
|
|
cfg.R2AccessKeyID,
|
|
cfg.R2AccessKeySecret,
|
|
"",
|
|
)),
|
|
awsconfig.WithRegion("auto"),
|
|
)
|
|
if err == nil {
|
|
h.s3Client = s3.NewFromConfig(awsCfg)
|
|
}
|
|
}
|
|
|
|
return h
|
|
}
|
|
|
|
func (h *UploadHandler) Upload(c *gin.Context) {
|
|
file, header, err := c.Request.FormFile("file")
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "no file uploaded"})
|
|
return
|
|
}
|
|
defer file.Close()
|
|
|
|
// 生成唯一文件名
|
|
ext := filepath.Ext(header.Filename)
|
|
filename := fmt.Sprintf("%d_%s%s", time.Now().UnixNano(), uuid.New().String()[:8], ext)
|
|
|
|
// 上传到 R2
|
|
if h.s3Client != nil {
|
|
key := "uploads/" + filename
|
|
|
|
_, err := h.s3Client.PutObject(context.TODO(), &s3.PutObjectInput{
|
|
Bucket: aws.String(h.cfg.R2BucketName),
|
|
Key: aws.String(key),
|
|
Body: file,
|
|
ContentType: aws.String(header.Header.Get("Content-Type")),
|
|
})
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to upload to R2"})
|
|
return
|
|
}
|
|
|
|
url := fmt.Sprintf("%s/%s", h.cfg.R2PublicURL, key)
|
|
c.JSON(http.StatusOK, gin.H{"url": url})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "no storage configured"})
|
|
}
|
|
|
|
func (h *UploadHandler) GetFile(c *gin.Context) {
|
|
filepath := c.Param("filepath")
|
|
filepath = strings.TrimPrefix(filepath, "/")
|
|
|
|
if h.s3Client == nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "file not found"})
|
|
return
|
|
}
|
|
|
|
result, err := h.s3Client.GetObject(context.TODO(), &s3.GetObjectInput{
|
|
Bucket: aws.String(h.cfg.R2BucketName),
|
|
Key: aws.String(filepath),
|
|
})
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "file not found"})
|
|
return
|
|
}
|
|
defer result.Body.Close()
|
|
|
|
if result.ContentType != nil {
|
|
c.Header("Content-Type", *result.ContentType)
|
|
}
|
|
|
|
io.Copy(c.Writer, result.Body)
|
|
}
|