Files
backend/internal/pkg/s3/s3.go
lan 4d8f2ec997 Initial backend repository commit.
Set up project files and add .gitignore to exclude local build/runtime artifacts.

Made-with: Cursor
2026-03-09 21:28:58 +08:00

120 lines
3.2 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 s3
import (
"bytes"
"context"
"fmt"
"time"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
"carrot_bbs/internal/config"
)
// Client S3客户端
type Client struct {
client *minio.Client
bucket string
domain string
}
// New 创建S3客户端
func New(cfg *config.S3Config) (*Client, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
client, err := minio.New(cfg.Endpoint, &minio.Options{
Creds: credentials.NewStaticV4(cfg.AccessKey, cfg.SecretKey, ""),
Secure: cfg.UseSSL,
})
if err != nil {
return nil, fmt.Errorf("failed to create S3 client: %w", err)
}
// 检查bucket是否存在
exists, err := client.BucketExists(ctx, cfg.Bucket)
if err != nil {
return nil, fmt.Errorf("failed to check bucket: %w", err)
}
if !exists {
if err := client.MakeBucket(ctx, cfg.Bucket, minio.MakeBucketOptions{
Region: cfg.Region,
}); err != nil {
return nil, fmt.Errorf("failed to create bucket: %w", err)
}
}
// 如果没有配置domain则使用默认的endpoint
domain := cfg.Domain
if domain == "" {
domain = cfg.Endpoint
}
return &Client{
client: client,
bucket: cfg.Bucket,
domain: domain,
}, nil
}
// Upload 上传文件
func (c *Client) Upload(ctx context.Context, objectName string, filePath string, contentType string) (string, error) {
_, err := c.client.FPutObject(ctx, c.bucket, objectName, filePath, minio.PutObjectOptions{
ContentType: contentType,
})
if err != nil {
return "", fmt.Errorf("failed to upload file: %w", err)
}
return fmt.Sprintf("%s/%s", c.bucket, objectName), nil
}
// UploadData 上传数据
func (c *Client) UploadData(ctx context.Context, objectName string, data []byte, contentType string) (string, error) {
_, err := c.client.PutObject(ctx, c.bucket, objectName, bytes.NewReader(data), int64(len(data)), minio.PutObjectOptions{
ContentType: contentType,
})
if err != nil {
return "", fmt.Errorf("failed to upload data: %w", err)
}
// 返回完整URL包含bucket名称
scheme := "https"
if c.domain == c.bucket || c.domain == "" {
scheme = "http"
}
return fmt.Sprintf("%s://%s/%s/%s", scheme, c.domain, c.bucket, objectName), nil
}
// GetURL 获取文件URL - 使用自定义域名
func (c *Client) GetURL(ctx context.Context, objectName string) (string, error) {
// 使用自定义域名构建URL包含bucket名称
scheme := "https"
if c.domain == c.bucket || c.domain == "" {
scheme = "http"
}
return fmt.Sprintf("%s://%s/%s/%s", scheme, c.domain, c.bucket, objectName), nil
}
// GetPresignedURL 获取预签名URL用于私有桶
func (c *Client) GetPresignedURL(ctx context.Context, objectName string) (string, error) {
url, err := c.client.PresignedGetObject(ctx, c.bucket, objectName, time.Hour*24, nil)
if err != nil {
return "", fmt.Errorf("failed to get presigned URL: %w", err)
}
return url.String(), nil
}
// Delete 删除文件
func (c *Client) Delete(ctx context.Context, objectName string) error {
return c.client.RemoveObject(ctx, c.bucket, objectName, minio.RemoveObjectOptions{})
}
// GetClient 获取原生客户端
func (c *Client) GetClient() *minio.Client {
return c.client
}