Files
backend/internal/pkg/s3/s3.go
lafay 2f584c1f2c
All checks were successful
Build Backend / build (push) Successful in 5m10s
Build Backend / build-docker (push) Successful in 2m22s
This is a breaking change that renames the entire project from "Carrot BBS" to "WithYou".
The Go module name has been changed from `carrot_bbs` to `with_you`, which requires
all import paths to be updated throughout the codebase and by external consumers.

BREAKING CHANGE: Module name changed from carrot_bbs to with_you. All imports
and dependencies must be updated from carrot_bbs/* to with_you/*.
2026-04-22 16:01:59 +08:00

132 lines
3.5 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"
"with_you/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
}
// TrustedPublicURLPrefix 本站公开对象 URL 前缀scheme://domain/bucket/),用于校验聊天图片等仅引用本地上传资源。
func (c *Client) TrustedPublicURLPrefix() string {
if c == nil || c.domain == "" || c.bucket == "" {
return ""
}
scheme := "https"
if c.domain == c.bucket {
scheme = "http"
}
return fmt.Sprintf("%s://%s/%s/", scheme, c.domain, c.bucket)
}
// 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
}