Files
2026-03-28 15:43:18 +08:00

142 lines
3.5 KiB
Go

package storage
import (
"context"
"errors"
"io"
"net/url"
"strings"
"time"
"filefast/backend/internal/config"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
)
type MinIOClient struct {
client *minio.Client
bucket string
presignExpiry time.Duration
enabled bool
}
func NewMinIOClient(cfg config.MinIOConfig) (*MinIOClient, error) {
if cfg.Endpoint == "" || cfg.AccessKey == "" || cfg.SecretKey == "" {
return &MinIOClient{
bucket: cfg.Bucket,
presignExpiry: cfg.PresignExpiry,
enabled: false,
}, nil
}
client, err := minio.New(cfg.Endpoint, &minio.Options{
Creds: credentials.NewStaticV4(cfg.AccessKey, cfg.SecretKey, ""),
Secure: cfg.UseSSL,
})
if err != nil {
return nil, err
}
return &MinIOClient{
client: client,
bucket: cfg.Bucket,
presignExpiry: cfg.PresignExpiry,
enabled: true,
}, nil
}
func (c *MinIOClient) Enabled() bool {
return c != nil && c.enabled
}
func (c *MinIOClient) EnsureBucket(ctx context.Context) error {
if !c.Enabled() {
return nil
}
exists, err := c.client.BucketExists(ctx, c.bucket)
if err != nil {
return err
}
if exists {
return nil
}
return c.client.MakeBucket(ctx, c.bucket, minio.MakeBucketOptions{})
}
func (c *MinIOClient) PresignUpload(ctx context.Context, objectKey string) (*url.URL, error) {
if !c.Enabled() {
return nil, errors.New("minio is disabled")
}
return c.client.PresignedPutObject(ctx, c.bucket, objectKey, c.presignExpiry)
}
func (c *MinIOClient) PresignDownload(ctx context.Context, objectKey string) (*url.URL, error) {
return c.PresignDownloadWithFilename(ctx, objectKey, "")
}
func (c *MinIOClient) PresignDownloadWithFilename(ctx context.Context, objectKey, filename string) (*url.URL, error) {
if !c.Enabled() {
return nil, errors.New("minio is disabled")
}
var reqParams url.Values
if filename = sanitizeDownloadFilename(filename); filename != "" {
reqParams = make(url.Values)
reqParams.Set("response-content-disposition", `attachment; filename="`+filename+`"`)
}
return c.client.PresignedGetObject(ctx, c.bucket, objectKey, c.presignExpiry, reqParams)
}
func (c *MinIOClient) UploadObject(ctx context.Context, objectKey string, reader io.Reader, size int64, contentType string) error {
if !c.Enabled() {
return errors.New("minio is disabled")
}
options := minio.PutObjectOptions{
ContentType: contentType,
}
_, err := c.client.PutObject(ctx, c.bucket, objectKey, reader, size, options)
return err
}
func (c *MinIOClient) OpenObject(ctx context.Context, objectKey string) (*minio.Object, minio.ObjectInfo, error) {
if !c.Enabled() {
return nil, minio.ObjectInfo{}, errors.New("minio is disabled")
}
object, err := c.client.GetObject(ctx, c.bucket, objectKey, minio.GetObjectOptions{})
if err != nil {
return nil, minio.ObjectInfo{}, err
}
info, err := object.Stat()
if err != nil {
_ = object.Close()
return nil, minio.ObjectInfo{}, err
}
return object, info, nil
}
func (c *MinIOClient) RemoveObject(ctx context.Context, objectKey string) error {
if !c.Enabled() {
return nil
}
return c.client.RemoveObject(ctx, c.bucket, objectKey, minio.RemoveObjectOptions{})
}
func sanitizeDownloadFilename(filename string) string {
filename = strings.TrimSpace(filename)
if filename == "" {
return ""
}
filename = strings.ReplaceAll(filename, `"`, "")
filename = strings.ReplaceAll(filename, "\r", "")
filename = strings.ReplaceAll(filename, "\n", "")
return filename
}