Files
common/minio/minio.go
qhd f319332751 minio修改图片上传-支持多图上传
mongo增加类型转换-将entity 转换为 bson.M
2026-03-12 08:51:17 +08:00

131 lines
4.7 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 minio
import (
"context"
"fmt"
"path/filepath"
"strings"
"time"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/google/uuid"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
)
// MinIOConfig 映射 YAML 中的 minio 配置节点
type MinIOConfig struct {
Endpoint string `yaml:"endpoint"` // MinIO API 地址
AccessKey string `yaml:"accessKey"` // AK
SecretKey string `yaml:"secretKey"` // SK
Secure bool `yaml:"secure"` // 是否启用 SSL
Region string `yaml:"region"` // 区域
BucketName string `yaml:"bucketName"` // 默认桶名
PresignedExpire string `yaml:"presignedExpire"` // 预签名URL过期时间如 5m、1h
}
// 全局 MinIO 客户端(初始化一次,避免重复创建)
var minioClient *minio.Client
var minioCfg MinIOConfig
// initMinIO 初始化 MinIO 客户端
func initMinIO(ctx context.Context) error {
var err error
// 加载 MinIO 配置(可从配置文件/环境变量读取,这里硬编码示例)
minioCfg = MinIOConfig{
Endpoint: g.Cfg().MustGet(ctx, "minio.endpoint").String(),
AccessKey: g.Cfg().MustGet(ctx, "minio.accessKey").String(),
SecretKey: g.Cfg().MustGet(ctx, "minio.secretKey").String(),
Secure: g.Cfg().MustGet(ctx, "minio.secure").Bool(),
Region: g.Cfg().MustGet(ctx, "minio.region").String(),
BucketName: g.Cfg().MustGet(ctx, "minio.bucketName").String(), // 专门存储图片的桶
}
// 创建 MinIO 客户端
minioClient, err = minio.New(minioCfg.Endpoint, &minio.Options{
Creds: credentials.NewStaticV4(minioCfg.AccessKey, minioCfg.SecretKey, ""),
Secure: minioCfg.Secure,
Region: minioCfg.Region,
})
if err != nil {
return fmt.Errorf("初始化 MinIO 客户端失败: %w", err)
}
// 检查/创建图片桶
exists, err := minioClient.BucketExists(ctx, minioCfg.BucketName)
if err != nil {
return fmt.Errorf("检查桶是否存在失败: %w", err)
}
if !exists {
err = minioClient.MakeBucket(ctx, minioCfg.BucketName, minio.MakeBucketOptions{Region: minioCfg.Region})
if err != nil {
return fmt.Errorf("创建桶失败: %w", err)
}
fmt.Printf("成功创建 MinIO 桶: %s\n", minioCfg.BucketName)
}
return nil
}
func UploadImages(ctx context.Context, fileHeaders []*ghttp.UploadFile) (imagesUrls []string, err error) {
imageUrls := make([]string, 0, len(fileHeaders)) // 存储上传后的图片访问地址
for _, fileHeader := range fileHeaders {
var imageUrl string
imageUrl, err = uploadImg(ctx, fileHeader)
if err != nil {
return nil, err
}
imageUrls = append(imageUrls, imageUrl)
}
return imageUrls, err
}
func UploadImage(ctx context.Context, fileHeader *ghttp.UploadFile) (imagesUrl string, err error) {
// 初始化 MinIO 客户端
err = initMinIO(ctx)
if err != nil {
return "", fmt.Errorf("初始化 MinIO 客户端失败: %w", err)
}
imageUrl, err := uploadImg(ctx, fileHeader)
if err != nil {
return "", err
}
return imageUrl, err
}
func uploadImg(ctx context.Context, fileHeader *ghttp.UploadFile) (imagesUrl string, err error) {
// 生成唯一的 MinIO 对象名(避免覆盖)
fileExt := filepath.Ext(fileHeader.Filename) // 原文件后缀(如 .jpg
uniqueID := uuid.New().String()[:8] // 8位随机UUID
timestamp := time.Now().Format("20060102") // 日期目录(便于管理)
objectName := fmt.Sprintf("images/%s/%s%s", timestamp, uniqueID, fileExt) // 存储路径images/20251209/abc12345.jpg
// 打开文件,获取 io.Reader*os.File 实现了 io.Reader
file, err := fileHeader.Open()
if err != nil { /* 处理错误 */
return "", fmt.Errorf("打开文件失败: %w", err)
}
defer file.Close() // 必须关闭,避免文件句柄泄露
// 设置存储桶公共读权限
policy := `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetObject"],"Resource":["arn:aws:s3:::` + minioCfg.BucketName + `/*"]}]}`
err = minioClient.SetBucketPolicy(context.Background(), minioCfg.BucketName, policy)
if err != nil {
return "", fmt.Errorf("设置存储桶权限失败: %w", err)
}
// 执行图片上传
_, err = minioClient.PutObject(
ctx,
minioCfg.BucketName,
objectName,
file,
fileHeader.Size,
minio.PutObjectOptions{
ContentType: "image/jpeg", // 关键指定图片MIME类型S3会根据此类型处理
// 若需要图片可公开访问,添加如下配置(根据需求选择)
//ACL: minio.ACLPublicRead,
},
)
if err != nil {
return "", fmt.Errorf("上传图片失败: %w", err)
}
replace := strings.Replace(objectName, "images/", "/", 1)
return replace, err
}