minioUploadLogic.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. // Code scaffolded by goctl. Safe to edit.
  2. // goctl 1.10.1
  3. package minio
  4. import (
  5. "context"
  6. "crypto/md5"
  7. "encoding/hex"
  8. "fmt"
  9. "io"
  10. "mime/multipart"
  11. "path/filepath"
  12. "slices"
  13. "strings"
  14. "time"
  15. "perms-system-server/internal/response"
  16. "perms-system-server/internal/svc"
  17. miniogo "github.com/minio/minio-go/v7"
  18. "github.com/zeromicro/go-zero/core/logx"
  19. )
  20. type MinioUploadResult struct {
  21. Url string `json:"url"`
  22. Path string `json:"path"`
  23. Md5 string `json:"md5"`
  24. Size int64 `json:"size"`
  25. }
  26. type MinioUploadLogic struct {
  27. logx.Logger
  28. ctx context.Context
  29. svcCtx *svc.ServiceContext
  30. }
  31. func NewMinioUploadLogic(ctx context.Context, svcCtx *svc.ServiceContext) *MinioUploadLogic {
  32. return &MinioUploadLogic{
  33. Logger: logx.WithContext(ctx),
  34. ctx: ctx,
  35. svcCtx: svcCtx,
  36. }
  37. }
  38. func (l *MinioUploadLogic) MinioUpload(fileHeader *multipart.FileHeader, file multipart.File, fileType string) (*MinioUploadResult, error) {
  39. if fileType == "" {
  40. return nil, response.ErrBadRequest("fileType is required")
  41. }
  42. minioFileType, ok := l.svcCtx.Config.Minio.FileType[fileType]
  43. if !ok {
  44. return nil, response.ErrBadRequest("fileType not configured")
  45. }
  46. if minioFileType.Bucket == "" {
  47. return nil, response.ErrBadRequest("bucket not configured")
  48. }
  49. contentType := fileHeader.Header.Get("Content-Type")
  50. if len(minioFileType.AllowedContentTypes) > 0 && minioFileType.AllowedContentTypes[0] != "" && minioFileType.AllowedContentTypes[0] != "*" {
  51. if !slices.Contains(minioFileType.AllowedContentTypes, contentType) {
  52. return nil, response.ErrBadRequest("invalid contentType: " + contentType)
  53. }
  54. }
  55. if err := l.ensureBucketExists(minioFileType.Bucket); err != nil {
  56. return nil, err
  57. }
  58. hash := md5.New()
  59. if _, err := io.Copy(hash, file); err != nil {
  60. return nil, response.ErrBadRequest("md5 计算失败: " + err.Error())
  61. }
  62. fileMd5 := hex.EncodeToString(hash.Sum(nil))
  63. file.Seek(0, io.SeekStart)
  64. fileExt := filepath.Ext(fileHeader.Filename)
  65. dir := strings.TrimSpace(parseDir(minioFileType.Dir))
  66. if dir != "" {
  67. dir += "/"
  68. }
  69. objectPath := fmt.Sprintf("%s%s%s", dir, fileMd5, fileExt)
  70. stat, statErr := l.svcCtx.MinioClient.StatObject(l.ctx, minioFileType.Bucket, objectPath, miniogo.StatObjectOptions{})
  71. if statErr == nil {
  72. objectFullPath := fmt.Sprintf("%s/%s", minioFileType.Bucket, objectPath)
  73. return &MinioUploadResult{
  74. Url: fmt.Sprintf("%s/%s", l.svcCtx.Config.Minio.Domain, objectFullPath),
  75. Path: objectFullPath,
  76. Md5: fileMd5,
  77. Size: stat.Size,
  78. }, nil
  79. }
  80. errCode := miniogo.ToErrorResponse(statErr).Code
  81. if errCode == "AccessDenied" || errCode == "NoSuchKey" {
  82. info, err := l.svcCtx.MinioClient.PutObject(l.ctx, minioFileType.Bucket, objectPath, file, fileHeader.Size, miniogo.PutObjectOptions{
  83. ContentType: contentType,
  84. })
  85. if err != nil {
  86. return nil, err
  87. }
  88. objectFullPath := fmt.Sprintf("%s/%s", minioFileType.Bucket, objectPath)
  89. return &MinioUploadResult{
  90. Url: fmt.Sprintf("%s/%s", l.svcCtx.Config.Minio.Domain, objectFullPath),
  91. Path: objectFullPath,
  92. Md5: fileMd5,
  93. Size: info.Size,
  94. }, nil
  95. }
  96. return nil, statErr
  97. }
  98. func (l *MinioUploadLogic) ensureBucketExists(bucket string) error {
  99. exists, err := l.svcCtx.MinioClient.BucketExists(l.ctx, bucket)
  100. if err != nil {
  101. return err
  102. }
  103. if !exists {
  104. return l.svcCtx.MinioClient.MakeBucket(l.ctx, bucket, miniogo.MakeBucketOptions{})
  105. }
  106. return nil
  107. }
  108. func parseDir(template string) string {
  109. now := time.Now()
  110. r := strings.NewReplacer(
  111. "{yyyy}", now.Format("2006"),
  112. "{mm}", now.Format("01"),
  113. "{dd}", now.Format("02"),
  114. )
  115. return r.Replace(template)
  116. }