| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166 |
- package audit
- import (
- "context"
- "fmt"
- "io"
- "os"
- "path/filepath"
- "sync/atomic"
- "time"
- "github.com/minio/minio-go/v7"
- "github.com/minio/minio-go/v7/pkg/credentials"
- )
- var (
- minioClient atomic.Value
- // clientSignature caches the config signature of the loaded client so a
- // hot reload that keeps the same endpoint/keys/bucket reuses the client
- // instead of rebuilding it on every request.
- clientSignature atomic.Value
- )
- // MinioClient wraps a minio client together with the target bucket.
- type MinioClient struct {
- Client *minio.Client
- BucketName string
- }
- // GetMinioClient atomically reads the global MinioClient (nil if not set).
- func GetMinioClient() *MinioClient {
- v := minioClient.Load()
- if v == nil {
- return nil
- }
- return v.(*MinioClient)
- }
- // SetMinioClient atomically writes the global MinioClient.
- func SetMinioClient(c *MinioClient) {
- minioClient.Store(c)
- }
- // newMinioClient builds a MinioClient and makes sure the bucket exists.
- func newMinioClient(endPoint, accessKey, secretKey, bucketName string, useSSl bool) (*MinioClient, error) {
- client, err := minio.New(endPoint, &minio.Options{
- Creds: credentials.NewStaticV4(accessKey, secretKey, ""),
- Secure: useSSl,
- })
- if err != nil {
- return nil, err
- }
- ctx := context.Background()
- exists, err := client.BucketExists(ctx, bucketName)
- if err != nil {
- return nil, err
- }
- if !exists {
- if err := client.MakeBucket(ctx, bucketName, minio.MakeBucketOptions{}); err != nil {
- return nil, err
- }
- }
- return &MinioClient{
- Client: client,
- BucketName: bucketName,
- }, nil
- }
- // ReloadMinioClient rebuilds the global client when the file configuration
- // changed. An empty configuration (before t_config has been populated) or a
- // failed connection keeps the previously loaded client untouched.
- func ReloadMinioClient(fileConfig FileConfigManager) {
- if fileConfig.endpoint == "" || fileConfig.bucket == "" ||
- fileConfig.accessKey == "" || fileConfig.secretKey == "" {
- return
- }
- signature := fmt.Sprintf("%s|%s|%s|%s|%t",
- fileConfig.endpoint, fileConfig.accessKey, fileConfig.secretKey, fileConfig.bucket, fileConfig.useSSL)
- if stored, ok := clientSignature.Load().(string); ok && stored == signature {
- return // configuration unchanged, keep the loaded client
- }
- client, err := newMinioClient(fileConfig.endpoint, fileConfig.accessKey, fileConfig.secretKey, fileConfig.bucket, fileConfig.useSSL)
- if err != nil {
- fmt.Printf("Minio client reload failed: %v, keep previous client\n", err)
- return
- }
- clientSignature.Store(signature)
- SetMinioClient(client)
- }
- // UploadFile uploads a local file and returns its object key.
- func (m *MinioClient) UploadFile(ctx context.Context, localFilePath, objectName string) (string, error) {
- file, err := os.Open(localFilePath)
- if err != nil {
- return "", err
- }
- defer func() {
- _ = file.Close()
- }()
- fileStat, err := file.Stat()
- if err != nil {
- return "", err
- }
- uploadInfo, err := m.Client.PutObject(ctx, m.BucketName, objectName, file, fileStat.Size(), minio.PutObjectOptions{})
- if err != nil {
- return "", err
- }
- return uploadInfo.Key, nil
- }
- // DownloadFile downloads an object into a local temp file and returns the
- // temp file path.
- func (m *MinioClient) DownloadFile(ctx context.Context, fileKey string) (string, error) {
- fileName := filepath.Base(fileKey)
- if fileName == "" || fileName == "." || fileName == "/" {
- return "", fmt.Errorf("无效的FileKey:%s", fileKey)
- }
- tmpFile, err := os.CreateTemp("", fmt.Sprintf("log_restore_*_%s", fileName))
- if err != nil {
- return "", fmt.Errorf("创建临时文件失败: %w", err)
- }
- defer func() {
- _ = tmpFile.Close()
- }()
- object, err := m.Client.GetObject(ctx, m.BucketName, fileKey, minio.GetObjectOptions{})
- if err != nil {
- return "", fmt.Errorf("从 MinIO 获取对象失败: %w", err)
- }
- defer object.Close()
- if _, err = io.Copy(tmpFile, object); err != nil {
- return "", fmt.Errorf("写入临时文件失败: %w", err)
- }
- return tmpFile.Name(), nil
- }
- // GetTempDownloadURL returns a presigned private-bucket download URL.
- func (m *MinioClient) GetTempDownloadURL(ctx context.Context, fileKey string, expire int32) (string, error) {
- url, err := m.Client.PresignedGetObject(ctx, m.BucketName, fileKey, time.Duration(expire)*time.Second, nil)
- if err != nil {
- return "", err
- }
- return url.String(), nil
- }
- // RemoveFile deletes an object from the bucket.
- func (m *MinioClient) RemoveFile(ctx context.Context, fileKey string) error {
- if fileKey == "" {
- return fmt.Errorf("无效的FileKey:%s", fileKey)
- }
- if err := m.Client.RemoveObject(ctx, m.BucketName, fileKey, minio.RemoveObjectOptions{}); err != nil {
- return fmt.Errorf("remove object failed: %w", err)
- }
- return nil
- }
|