Files
X-Agents/server/internal/service/upload_service.go

167 lines
4.8 KiB
Go
Raw Normal View History

package service
import (
"context"
"fmt"
"io"
"mime/multipart"
"net/url"
"os"
"path/filepath"
"strings"
"time"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
"github.com/google/uuid"
"x-agents/server/internal/config"
)
type UploadService struct {
cfg *config.Config
minioClient *minio.Client
}
// UploadResponse 上传响应
type UploadResponse struct {
Success bool `json:"success"`
URL string `json:"url"`
FileKey string `json:"fileKey"` // 文件标识,用于删除等操作
Message string `json:"message"`
}
func NewUploadService(cfg *config.Config) (*UploadService, error) {
s := &UploadService{cfg: cfg}
// 如果是 MinIO 模式,初始化 MinIO 客户端
if cfg.UploadMode == "minio" {
if cfg.MinIOEndpoint == "" || cfg.MinIOAccessKey == "" || cfg.MinIOSecretKey == "" {
return nil, fmt.Errorf("MinIO configuration is incomplete")
}
client, err := minio.New(cfg.MinIOEndpoint, &minio.Options{
Creds: credentials.NewStaticV4(cfg.MinIOAccessKey, cfg.MinIOSecretKey, ""),
Secure: cfg.MinIOUseSSL,
})
if err != nil {
return nil, fmt.Errorf("failed to create MinIO client: %w", err)
}
// 检查 bucket 是否存在,不存在则创建
exists, err := client.BucketExists(context.Background(), cfg.MinIOBucket)
if err != nil {
return nil, fmt.Errorf("failed to check MinIO bucket: %w", err)
}
if !exists {
if err := client.MakeBucket(context.Background(), cfg.MinIOBucket, minio.MakeBucketOptions{}); err != nil {
return nil, fmt.Errorf("failed to create MinIO bucket: %w", err)
}
}
s.minioClient = client
}
return s, nil
}
// Upload 上传文件
func (s *UploadService) Upload(file *multipart.FileHeader) (*UploadResponse, error) {
// 打开文件
f, err := file.Open()
if err != nil {
return &UploadResponse{Success: false, Message: err.Error()}, nil
}
defer f.Close()
// 生成唯一文件名
ext := filepath.Ext(file.Filename)
filename := fmt.Sprintf("%s%s", uuid.New().String(), ext)
fileKey := strings.TrimSuffix(filename, ext)
// 根据模式选择存储方式
if s.cfg.UploadMode == "minio" {
return s.uploadToMinIO(f, filename, fileKey, file.Size)
}
return s.uploadToLocal(f, filename, fileKey)
}
// uploadToMinIO 上传到 MinIO
func (s *UploadService) uploadToMinIO(f multipart.File, filename, fileKey string, size int64) (*UploadResponse, error) {
_, err := s.minioClient.PutObject(context.Background(), s.cfg.MinIOBucket, filename, f, size, minio.PutObjectOptions{
ContentType: "application/octet-stream",
})
if err != nil {
return &UploadResponse{Success: false, Message: fmt.Sprintf("MinIO upload failed: %v", err)}, nil
}
// 生成预签名 URL有效期 24 小时)
presignedURL, err := s.minioClient.PresignedGetObject(context.Background(), s.cfg.MinIOBucket, filename, 24*time.Hour, nil)
if err != nil {
return &UploadResponse{Success: false, Message: fmt.Sprintf("failed to generate URL: %v", err)}, nil
}
return &UploadResponse{
Success: true,
URL: presignedURL.String(),
FileKey: fileKey,
Message: "Upload successful",
}, nil
}
// uploadToLocal 上传到本地
func (s *UploadService) uploadToLocal(f multipart.File, filename, fileKey string) (*UploadResponse, error) {
// 确保目录存在
uploadPath := s.cfg.UploadLocalPath
if err := os.MkdirAll(uploadPath, 0755); err != nil {
return &UploadResponse{Success: false, Message: fmt.Sprintf("failed to create directory: %v", err)}, nil
}
// 创建文件
dst, err := os.Create(filepath.Join(uploadPath, filename))
if err != nil {
return &UploadResponse{Success: false, Message: err.Error()}, nil
}
defer dst.Close()
// 复制内容
if _, err := io.Copy(dst, f); err != nil {
return &UploadResponse{Success: false, Message: err.Error()}, nil
}
// 生成 URL
fileURL := fmt.Sprintf("%s/files/%s", s.cfg.ServerBaseURL, filename)
return &UploadResponse{
Success: true,
URL: fileURL,
FileKey: fileKey,
Message: "Upload successful",
}, nil
}
// DeleteFile 删除文件
func (s *UploadService) DeleteFile(filename string) error {
if s.cfg.UploadMode == "minio" {
return s.minioClient.RemoveObject(context.Background(), s.cfg.MinIOBucket, filename, minio.RemoveObjectOptions{})
}
// 本地删除
path := filepath.Join(s.cfg.UploadLocalPath, filename)
return os.Remove(path)
}
// GetFileURL 获取文件 URL用于已存在的文件
func (s *UploadService) GetFileURL(filename string) (string, error) {
if s.cfg.UploadMode == "minio" {
presignedURL, err := s.minioClient.PresignedGetObject(context.Background(), s.cfg.MinIOBucket, filename, 24*time.Hour, nil)
if err != nil {
return "", err
}
return presignedURL.String(), nil
}
// 本地文件
escaped := url.PathEscape(filename)
return fmt.Sprintf("%s/files/%s", s.cfg.ServerBaseURL, escaped), nil
}