Initial backend repository commit.
Set up project files and add .gitignore to exclude local build/runtime artifacts. Made-with: Cursor
This commit is contained in:
119
internal/pkg/s3/s3.go
Normal file
119
internal/pkg/s3/s3.go
Normal file
@@ -0,0 +1,119 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user