minioUploadLogic.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. // Code scaffolded by goctl. Safe to edit.
  2. // goctl 1.10.1
  3. package auth
  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. "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. src, err := fileHeader.Open()
  59. if err != nil {
  60. return nil, err
  61. }
  62. defer src.Close()
  63. hash := md5.New()
  64. if _, err := io.Copy(hash, src); err != nil {
  65. return nil, response.ErrBadRequest("md5 计算失败: " + err.Error())
  66. }
  67. fileMd5 := hex.EncodeToString(hash.Sum(nil))
  68. src.Seek(0, io.SeekStart)
  69. fileExt := filepath.Ext(fileHeader.Filename)
  70. dir := strings.TrimSpace(parseDir(minioFileType.Dir))
  71. if dir != "" {
  72. dir += "/"
  73. }
  74. objectPath := fmt.Sprintf("%s%s%s", dir, fileMd5, fileExt)
  75. stat, statErr := l.svcCtx.MinioClient.StatObject(l.ctx, minioFileType.Bucket, objectPath, minio.StatObjectOptions{})
  76. if statErr == nil {
  77. objectFullPath := fmt.Sprintf("%s/%s", minioFileType.Bucket, objectPath)
  78. return &MinioUploadResult{
  79. Url: fmt.Sprintf("%s/%s", l.svcCtx.Config.Minio.Domain, objectFullPath),
  80. Path: objectFullPath,
  81. Md5: fileMd5,
  82. Size: stat.Size,
  83. }, nil
  84. }
  85. errCode := minio.ToErrorResponse(statErr).Code
  86. if errCode == "AccessDenied" || errCode == "NoSuchKey" {
  87. info, err := l.svcCtx.MinioClient.PutObject(l.ctx, minioFileType.Bucket, objectPath, src, fileHeader.Size, minio.PutObjectOptions{
  88. ContentType: contentType,
  89. })
  90. if err != nil {
  91. return nil, err
  92. }
  93. objectFullPath := fmt.Sprintf("%s/%s", minioFileType.Bucket, objectPath)
  94. return &MinioUploadResult{
  95. Url: fmt.Sprintf("%s/%s", l.svcCtx.Config.Minio.Domain, objectFullPath),
  96. Path: objectFullPath,
  97. Md5: fileMd5,
  98. Size: info.Size,
  99. }, nil
  100. }
  101. return nil, statErr
  102. }
  103. func (l *MinioUploadLogic) ensureBucketExists(bucket string) error {
  104. exists, err := l.svcCtx.MinioClient.BucketExists(l.ctx, bucket)
  105. if err != nil {
  106. return err
  107. }
  108. if !exists {
  109. return l.svcCtx.MinioClient.MakeBucket(l.ctx, bucket, minio.MakeBucketOptions{})
  110. }
  111. return nil
  112. }
  113. func parseDir(template string) string {
  114. now := time.Now()
  115. r := strings.NewReplacer(
  116. "{yyyy}", now.Format("2006"),
  117. "{mm}", now.Format("01"),
  118. "{dd}", now.Format("02"),
  119. )
  120. return r.Replace(template)
  121. }