You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

minio_ext.go 11 kB

5 years ago
5 years ago
4 years ago
5 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. package storage
  2. import (
  3. "encoding/xml"
  4. "errors"
  5. "sort"
  6. "strconv"
  7. "strings"
  8. "sync"
  9. "time"
  10. "code.gitea.io/gitea/modules/log"
  11. "code.gitea.io/gitea/modules/minio_ext"
  12. "code.gitea.io/gitea/modules/setting"
  13. miniov6 "github.com/minio/minio-go/v6"
  14. )
  15. const (
  16. PresignedUploadPartUrlExpireTime = time.Hour * 24 * 7
  17. )
  18. type ComplPart struct {
  19. PartNumber int `json:"partNumber"`
  20. ETag string `json:"eTag"`
  21. }
  22. type CompleteParts struct {
  23. Data []ComplPart `json:"completedParts"`
  24. }
  25. // completedParts is a collection of parts sortable by their part numbers.
  26. // used for sorting the uploaded parts before completing the multipart request.
  27. type completedParts []miniov6.CompletePart
  28. func (a completedParts) Len() int { return len(a) }
  29. func (a completedParts) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
  30. func (a completedParts) Less(i, j int) bool { return a[i].PartNumber < a[j].PartNumber }
  31. // completeMultipartUpload container for completing multipart upload.
  32. type completeMultipartUpload struct {
  33. XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ CompleteMultipartUpload" json:"-"`
  34. Parts []miniov6.CompletePart `xml:"Part"`
  35. }
  36. var (
  37. adminClient *minio_ext.Client = nil
  38. coreClient *miniov6.Core = nil
  39. )
  40. var mutex *sync.Mutex
  41. func init() {
  42. mutex = new(sync.Mutex)
  43. }
  44. func getClients() (*minio_ext.Client, *miniov6.Core, error) {
  45. var client *minio_ext.Client
  46. var core *miniov6.Core
  47. mutex.Lock()
  48. defer mutex.Unlock()
  49. if nil != adminClient && nil != coreClient {
  50. client = adminClient
  51. core = coreClient
  52. return client, core, nil
  53. }
  54. var err error
  55. minio := setting.Attachment.Minio
  56. if nil == adminClient {
  57. adminClient, err = minio_ext.New(
  58. minio.Endpoint,
  59. minio.AccessKeyID,
  60. minio.SecretAccessKey,
  61. minio.UseSSL,
  62. )
  63. if nil != err {
  64. return nil, nil, err
  65. }
  66. }
  67. client = adminClient
  68. if nil == coreClient {
  69. coreClient, err = miniov6.NewCore(
  70. minio.Endpoint,
  71. minio.AccessKeyID,
  72. minio.SecretAccessKey,
  73. minio.UseSSL,
  74. )
  75. if nil != err {
  76. return nil, nil, err
  77. }
  78. }
  79. core = coreClient
  80. return client, core, nil
  81. }
  82. func GenMultiPartSignedUrl(objectName string, uploadId string, partNumber int, partSize int64) (string, error) {
  83. minioClient, _, err := getClients()
  84. if err != nil {
  85. log.Error("getClients failed:", err.Error())
  86. return "", err
  87. }
  88. minio := setting.Attachment.Minio
  89. bucketName := minio.Bucket
  90. //objectName := strings.TrimPrefix(path.Join(minio.BasePath, path.Join(uuid[0:1], uuid[1:2], uuid)), "/")
  91. return minioClient.GenUploadPartSignedUrl(uploadId, bucketName, objectName, partNumber, partSize, PresignedUploadPartUrlExpireTime, setting.Attachment.Minio.Location)
  92. }
  93. func GetAllObjectByBucketAndPrefixMinio(bucket string, prefix string) ([]FileInfo, error) {
  94. _, core, err := getClients()
  95. if err != nil {
  96. log.Error("getClients failed:", err.Error())
  97. return nil, err
  98. }
  99. prefixLen := len(prefix)
  100. delimiter := ""
  101. marker := ""
  102. index := 1
  103. fileInfoList := FileInfoList{}
  104. for {
  105. output, err := core.ListObjects(bucket, prefix, marker, delimiter, 1000)
  106. if err == nil {
  107. log.Info("Page:%d\n", index)
  108. index++
  109. for _, val := range output.Contents {
  110. var isDir bool
  111. if prefixLen == len(val.Key) {
  112. continue
  113. }
  114. if strings.HasSuffix(val.Key, "/") {
  115. isDir = true
  116. } else {
  117. isDir = false
  118. }
  119. fileInfo := FileInfo{
  120. ModTime: val.LastModified.Format("2006-01-02 15:04:05"),
  121. FileName: val.Key[prefixLen:],
  122. Size: val.Size,
  123. IsDir: isDir,
  124. ParenDir: "",
  125. }
  126. fileInfoList = append(fileInfoList, fileInfo)
  127. }
  128. if output.IsTruncated {
  129. marker = output.NextMarker
  130. } else {
  131. break
  132. }
  133. } else {
  134. log.Info("list error." + err.Error())
  135. return nil, err
  136. }
  137. }
  138. sort.Sort(fileInfoList)
  139. return fileInfoList, nil
  140. }
  141. func GetOneLevelAllObjectUnderDirMinio(bucket string, prefixRootPath string, relativePath string) ([]FileInfo, error) {
  142. _, core, err := getClients()
  143. if err != nil {
  144. log.Error("getClients failed:", err.Error())
  145. return nil, err
  146. }
  147. Prefix := prefixRootPath + relativePath
  148. if !strings.HasSuffix(Prefix, "/") {
  149. Prefix += "/"
  150. }
  151. log.Info("bucket=" + bucket + " Prefix=" + Prefix)
  152. output, err := core.ListObjects(bucket, Prefix, "", "", 1000)
  153. fileInfos := make([]FileInfo, 0)
  154. prefixLen := len(Prefix)
  155. fileMap := make(map[string]bool, 0)
  156. if err == nil {
  157. for _, val := range output.Contents {
  158. log.Info("val key=" + val.Key)
  159. var isDir bool
  160. var fileName string
  161. if val.Key == Prefix {
  162. continue
  163. }
  164. fileName = val.Key[prefixLen:]
  165. log.Info("fileName =" + fileName)
  166. files := strings.Split(fileName, "/")
  167. if fileMap[files[0]] {
  168. continue
  169. } else {
  170. fileMap[files[0]] = true
  171. }
  172. ParenDir := relativePath
  173. fileName = files[0]
  174. if len(files) > 1 {
  175. isDir = true
  176. ParenDir += fileName + "/"
  177. } else {
  178. isDir = false
  179. }
  180. fileInfo := FileInfo{
  181. ModTime: val.LastModified.Local().Format("2006-01-02 15:04:05"),
  182. FileName: fileName,
  183. Size: val.Size,
  184. IsDir: isDir,
  185. ParenDir: ParenDir,
  186. }
  187. fileInfos = append(fileInfos, fileInfo)
  188. }
  189. return fileInfos, err
  190. } else {
  191. log.Error("Message:%s", err.Error())
  192. return nil, err
  193. }
  194. }
  195. func MinioGetFilesSize(bucketName string, Files []string) int64 {
  196. _, core, err := getClients()
  197. var fileTotalSize int64
  198. fileTotalSize = 0
  199. if err != nil {
  200. log.Error("getClients failed:", err.Error())
  201. return fileTotalSize
  202. }
  203. for _, file := range Files {
  204. log.Info("file=" + file)
  205. meta, err := core.StatObject(bucketName, file, miniov6.StatObjectOptions{})
  206. if err != nil {
  207. log.Info("Get file error:" + err.Error())
  208. }
  209. fileTotalSize += meta.Size
  210. }
  211. return fileTotalSize
  212. }
  213. func MinioCopyFiles(bucketName string, srcPath string, destPath string, Files []string) (int64, error) {
  214. _, core, err := getClients()
  215. var fileTotalSize int64
  216. fileTotalSize = 0
  217. if err != nil {
  218. log.Error("getClients failed:", err.Error())
  219. return fileTotalSize, err
  220. }
  221. for _, file := range Files {
  222. srcObjectName := srcPath + file
  223. destObjectName := destPath + file
  224. log.Info("srcObjectName=" + srcObjectName + " destObjectName=" + destObjectName)
  225. meta, err := core.StatObject(bucketName, srcObjectName, miniov6.StatObjectOptions{})
  226. if err != nil {
  227. log.Info("Get file error:" + err.Error())
  228. }
  229. core.CopyObject(bucketName, srcObjectName, bucketName, destObjectName, meta.UserMetadata)
  230. fileTotalSize += meta.Size
  231. }
  232. return fileTotalSize, nil
  233. }
  234. func MinioCopyAFile(srcBucketName, srcObjectName, destBucketName, destObjectName string) (int64, error) {
  235. _, core, err := getClients()
  236. var fileTotalSize int64
  237. fileTotalSize = 0
  238. if err != nil {
  239. log.Error("getClients failed:", err.Error())
  240. return fileTotalSize, err
  241. }
  242. meta, err := core.StatObject(srcBucketName, srcObjectName, miniov6.StatObjectOptions{})
  243. if err != nil {
  244. log.Info("Get file error:" + err.Error())
  245. }
  246. core.CopyObject(srcBucketName, srcObjectName, destBucketName, destObjectName, meta.UserMetadata)
  247. fileTotalSize = meta.Size
  248. return fileTotalSize, nil
  249. }
  250. func MinioPathCopy(bucketName string, srcPath string, destPath string) (int64, error) {
  251. _, core, err := getClients()
  252. var fileTotalSize int64
  253. fileTotalSize = 0
  254. if err != nil {
  255. log.Error("getClients failed:", err.Error())
  256. return fileTotalSize, err
  257. }
  258. delimiter := ""
  259. marker := ""
  260. for {
  261. output, err := core.ListObjects(bucketName, srcPath, marker, delimiter, 1000)
  262. if err == nil {
  263. for _, val := range output.Contents {
  264. srcObjectName := val.Key
  265. destObjectName := destPath + srcObjectName[len(srcPath):]
  266. log.Info("srcObjectName=" + srcObjectName + " destObjectName=" + destObjectName)
  267. core.CopyObject(bucketName, srcObjectName, bucketName, destObjectName, val.UserMetadata)
  268. fileTotalSize += val.Size
  269. }
  270. if output.IsTruncated {
  271. marker = output.NextMarker
  272. } else {
  273. break
  274. }
  275. } else {
  276. log.Info("list error." + err.Error())
  277. return 0, err
  278. }
  279. }
  280. return fileTotalSize, nil
  281. }
  282. func NewMultiPartUpload(objectName string) (string, error) {
  283. _, core, err := getClients()
  284. if err != nil {
  285. log.Error("getClients failed:", err.Error())
  286. return "", err
  287. }
  288. minio := setting.Attachment.Minio
  289. bucketName := minio.Bucket
  290. //objectName := strings.TrimPrefix(path.Join(minio.BasePath, path.Join(uuid[0:1], uuid[1:2], uuid)), "/")
  291. return core.NewMultipartUpload(bucketName, objectName, miniov6.PutObjectOptions{})
  292. }
  293. func CompleteMultiPartUpload(objectName string, uploadID string, totalChunks int) (string, error) {
  294. client, core, err := getClients()
  295. if err != nil {
  296. log.Error("getClients failed:", err.Error())
  297. return "", err
  298. }
  299. minio := setting.Attachment.Minio
  300. bucketName := minio.Bucket
  301. //objectName := strings.TrimPrefix(path.Join(minio.BasePath, path.Join(uuid[0:1], uuid[1:2], uuid)), "/")
  302. log.Info("bucketName=" + bucketName + " objectName=" + objectName + " uploadID=" + uploadID)
  303. partInfos, err := client.ListObjectParts(bucketName, objectName, uploadID)
  304. if err != nil {
  305. log.Error("ListObjectParts failed:", err.Error())
  306. return "", err
  307. }
  308. if len(partInfos) != totalChunks {
  309. log.Error("ListObjectParts number(%d) is not equal the set total chunk number(%d)", len(partInfos), totalChunks)
  310. return "", errors.New("the parts is not complete")
  311. }
  312. var complMultipartUpload completeMultipartUpload
  313. for _, partInfo := range partInfos {
  314. complMultipartUpload.Parts = append(complMultipartUpload.Parts, miniov6.CompletePart{
  315. PartNumber: partInfo.PartNumber,
  316. ETag: partInfo.ETag,
  317. })
  318. }
  319. // Sort all completed parts.
  320. sort.Sort(completedParts(complMultipartUpload.Parts))
  321. return core.CompleteMultipartUpload(bucketName, objectName, uploadID, complMultipartUpload.Parts)
  322. }
  323. func GetPartInfos(objectName string, uploadID string) (string, error) {
  324. minioClient, _, err := getClients()
  325. if err != nil {
  326. log.Error("getClients failed:", err.Error())
  327. return "", err
  328. }
  329. minio := setting.Attachment.Minio
  330. bucketName := minio.Bucket
  331. //objectName := strings.TrimPrefix(path.Join(minio.BasePath, path.Join(uuid[0:1], uuid[1:2], uuid)), "/")
  332. partInfos, err := minioClient.ListObjectParts(bucketName, objectName, uploadID)
  333. if err != nil {
  334. log.Error("ListObjectParts failed:", err.Error())
  335. return "", err
  336. }
  337. var chunks string
  338. for _, partInfo := range partInfos {
  339. chunks += strconv.Itoa(partInfo.PartNumber) + "-" + partInfo.ETag + ","
  340. }
  341. return chunks, nil
  342. }
  343. func IsObjectExist4Minio(bucket, objectName string) (bool, error) {
  344. _, core, err := getClients()
  345. if err != nil {
  346. log.Error("getClients failed:", err.Error())
  347. return false, err
  348. }
  349. _, err = core.StatObject(bucket, objectName, miniov6.StatObjectOptions{})
  350. if err != nil {
  351. log.Error("GetObjectMetadata error.%v", err)
  352. return false, err
  353. }
  354. return true, nil
  355. }