// Code scaffolded by goctl. Safe to edit. // goctl 1.10.1 package auth import ( "context" "crypto/md5" "encoding/hex" "fmt" "io" "mime/multipart" "path/filepath" "slices" "strings" "time" "perms-system-server/internal/response" "perms-system-server/internal/svc" "github.com/minio/minio-go/v7" "github.com/zeromicro/go-zero/core/logx" ) type MinioUploadResult struct { Url string `json:"url"` Path string `json:"path"` Md5 string `json:"md5"` Size int64 `json:"size"` } type MinioUploadLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } func NewMinioUploadLogic(ctx context.Context, svcCtx *svc.ServiceContext) *MinioUploadLogic { return &MinioUploadLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } func (l *MinioUploadLogic) MinioUpload(fileHeader *multipart.FileHeader, file multipart.File, fileType string) (*MinioUploadResult, error) { if fileType == "" { return nil, response.ErrBadRequest("fileType is required") } minioFileType, ok := l.svcCtx.Config.Minio.FileType[fileType] if !ok { return nil, response.ErrBadRequest("fileType not configured") } if minioFileType.Bucket == "" { return nil, response.ErrBadRequest("bucket not configured") } contentType := fileHeader.Header.Get("Content-Type") if len(minioFileType.AllowedContentTypes) > 0 && minioFileType.AllowedContentTypes[0] != "" && minioFileType.AllowedContentTypes[0] != "*" { if !slices.Contains(minioFileType.AllowedContentTypes, contentType) { return nil, response.ErrBadRequest("invalid contentType: " + contentType) } } if err := l.ensureBucketExists(minioFileType.Bucket); err != nil { return nil, err } hash := md5.New() if _, err := io.Copy(hash, file); err != nil { return nil, response.ErrBadRequest("md5 计算失败: " + err.Error()) } fileMd5 := hex.EncodeToString(hash.Sum(nil)) file.Seek(0, io.SeekStart) fileExt := filepath.Ext(fileHeader.Filename) dir := strings.TrimSpace(parseDir(minioFileType.Dir)) if dir != "" { dir += "/" } objectPath := fmt.Sprintf("%s%s%s", dir, fileMd5, fileExt) stat, statErr := l.svcCtx.MinioClient.StatObject(l.ctx, minioFileType.Bucket, objectPath, minio.StatObjectOptions{}) if statErr == nil { objectFullPath := fmt.Sprintf("%s/%s", minioFileType.Bucket, objectPath) return &MinioUploadResult{ Url: fmt.Sprintf("%s/%s", l.svcCtx.Config.Minio.Domain, objectFullPath), Path: objectFullPath, Md5: fileMd5, Size: stat.Size, }, nil } errCode := minio.ToErrorResponse(statErr).Code if errCode == "AccessDenied" || errCode == "NoSuchKey" { info, err := l.svcCtx.MinioClient.PutObject(l.ctx, minioFileType.Bucket, objectPath, file, fileHeader.Size, minio.PutObjectOptions{ ContentType: contentType, }) if err != nil { return nil, err } objectFullPath := fmt.Sprintf("%s/%s", minioFileType.Bucket, objectPath) return &MinioUploadResult{ Url: fmt.Sprintf("%s/%s", l.svcCtx.Config.Minio.Domain, objectFullPath), Path: objectFullPath, Md5: fileMd5, Size: info.Size, }, nil } return nil, statErr } func (l *MinioUploadLogic) ensureBucketExists(bucket string) error { exists, err := l.svcCtx.MinioClient.BucketExists(l.ctx, bucket) if err != nil { return err } if !exists { return l.svcCtx.MinioClient.MakeBucket(l.ctx, bucket, minio.MakeBucketOptions{}) } return nil } func parseDir(template string) string { now := time.Now() r := strings.NewReplacer( "{yyyy}", now.Format("2006"), "{mm}", now.Format("01"), "{dd}", now.Format("02"), ) return r.Replace(template) }