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