120 lines
3.2 KiB
Go
120 lines
3.2 KiB
Go
|
|
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
|
|||
|
|
}
|