store_minio.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. package store
  2. import (
  3. "bytes"
  4. "context"
  5. "errors"
  6. "github.com/minio/minio-go"
  7. "io"
  8. "io/ioutil"
  9. "net/http"
  10. "net/url"
  11. "strings"
  12. "sync"
  13. )
  14. var once sync.Once
  15. // ErrorInvalidName 无效的文件名
  16. var ErrorInvalidName = errors.New("invalid file name")
  17. // MinioStore minio 对象存储
  18. type MinioStore struct {
  19. cli *minio.Client
  20. }
  21. // ComposeObject 通过使用服务端拷贝实现钭多个源对象合并创建成一个新的对象。
  22. func (m *MinioStore) ComposeObject(ctx context.Context,pathS []string,filePath string) error {
  23. bucketName,fileName,err := m.parseFilename(filePath)
  24. if err != nil{
  25. return err
  26. }
  27. dst, err := minio.NewDestinationInfo(bucketName, fileName, nil, nil)
  28. if err != nil {
  29. return err
  30. }
  31. srcs := make([]minio.SourceInfo,0,len(pathS))
  32. for i:=range pathS{
  33. bucketName,fileName,err := m.parseFilename(pathS[i])
  34. if err != nil{
  35. return err
  36. }
  37. srcs = append(srcs,minio.NewSourceInfo(bucketName, fileName, nil))
  38. }
  39. return m.cli.ComposeObject(dst, srcs)
  40. }
  41. //RemoveObject 删除minio中的文件
  42. func (m *MinioStore) RemoveObject(ctx context.Context,filePath string) error {
  43. bucketName,fileName,err := m.parseFilename(filePath)
  44. if err != nil{
  45. return err
  46. }
  47. return m.cli.RemoveObject(bucketName, fileName)
  48. }
  49. func (m *MinioStore) Delete(ctx context.Context, fileName string) error {
  50. b, o, err := m.parseFilename(fileName)
  51. if err != nil {
  52. return err
  53. }
  54. n, err := url.PathUnescape(o)
  55. if err != nil {
  56. return err
  57. }
  58. return m.cli.RemoveObject(b, n)
  59. }
  60. // Stat 文件状态信息
  61. func (m *MinioStore) Stat(filename string) (minio.ObjectInfo, error) {
  62. bucketName, objectName, err := m.parseFilename(filename)
  63. if err != nil {
  64. return minio.ObjectInfo{}, err
  65. }
  66. return m.cli.StatObject(bucketName, objectName, minio.StatObjectOptions{})
  67. }
  68. // Get get file buffers
  69. func (m *MinioStore) Get(ctx context.Context, fileName string) ([]byte, string, error) {
  70. if ctx == nil {
  71. ctx = context.Background()
  72. }
  73. stat, err := m.Stat(fileName)
  74. if err != nil {
  75. return nil, "", errors.New("文件不存在:" + fileName)
  76. }
  77. bucketName, objectName, err := m.parseFilename(fileName)
  78. if err != nil {
  79. return nil, "", err
  80. }
  81. obj, err := m.cli.GetObjectWithContext(ctx, bucketName, objectName, minio.GetObjectOptions{})
  82. if err != nil {
  83. return nil, "", err
  84. }
  85. buf, err := ioutil.ReadAll(obj)
  86. if err != nil {
  87. return nil, "", err
  88. }
  89. return buf, stat.ContentType, nil
  90. }
  91. // Store save file
  92. func (m *MinioStore) Store(ctx context.Context, filename string, data io.Reader, size int64) (fileHash string, err error) {
  93. if ctx == nil {
  94. ctx = context.Background()
  95. }
  96. bucket, objName, err := m.parseFilename(filename)
  97. if err != nil {
  98. return
  99. }
  100. exists, err := m.cli.BucketExists(bucket)
  101. if err != nil {
  102. return
  103. } else if !exists {
  104. err = m.cli.MakeBucket(bucket, "local")
  105. if err != nil {
  106. return
  107. }
  108. }
  109. buf, err := ioutil.ReadAll(data)
  110. if err != nil {
  111. return
  112. }
  113. rd := bytes.NewBuffer(buf)
  114. if size == 0 {
  115. size = int64(rd.Len())
  116. }
  117. _, err = m.cli.PutObjectWithContext(ctx, bucket, objName, rd, size, minio.PutObjectOptions{
  118. ContentType: http.DetectContentType(buf),
  119. NumThreads: 2,
  120. })
  121. stat, err := m.Stat(filename)
  122. if err != nil {
  123. return
  124. }
  125. fileHash = stat.ETag
  126. return
  127. }
  128. // 解析文件名 `prefix/bucket/uuid/filename`
  129. func (m *MinioStore) parseFilename(filename string) (string, string, error) {
  130. if len(filename) > 0 && filename[0] == '/' {
  131. filename = filename[1:]
  132. }
  133. names := strings.Split(filename, "/")
  134. if len(names) < 3 {
  135. return "", "", ErrorInvalidName
  136. }
  137. return strings.ToLower(names[1]), strings.Join(names[2:], "/"), nil
  138. }
  139. // MiniStoreInit minio init
  140. func MiniStoreInit(addr, accessKey, secretKey string) *MinioStore {
  141. ms := new(MinioStore)
  142. once.Do(func() {
  143. cli, err := minio.New(addr, accessKey, secretKey, false)
  144. if err != nil {
  145. panic(err)
  146. }
  147. ms.cli = cli
  148. })
  149. return ms
  150. }