// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package storage import ( "io" "net/url" "path" "strings" "time" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "github.com/minio/minio-go" ) var ( _ ObjectStorage = &MinioStorage{} ) const ( PresignedGetUrlExpireTime = time.Hour * 24 * 7 PresignedPutUrlExpireTime = time.Hour * 24 * 7 ) // MinioStorage returns a minio bucket storage type MinioStorage struct { client *minio.Client bucket string basePath string } // NewMinioStorage returns a minio storage func NewMinioStorage(endpoint, accessKeyID, secretAccessKey, bucket, location, basePath string, useSSL bool) (*MinioStorage, error) { minioClient, err := minio.New(endpoint, accessKeyID, secretAccessKey, useSSL) if err != nil { return nil, err } if err := minioClient.MakeBucket(bucket, location); err != nil { // Check to see if we already own this bucket (which happens if you run this twice) exists, errBucketExists := minioClient.BucketExists(bucket) if !exists || errBucketExists != nil { return nil, err } } return &MinioStorage{ client: minioClient, bucket: bucket, basePath: basePath, }, nil } func (m *MinioStorage) buildMinioPath(p string) string { return strings.TrimPrefix(path.Join(m.basePath, p), "/") } func (m *MinioStorage) DownloadAFile(bucket string, objectName string) (io.ReadCloser, error) { var opts = minio.GetObjectOptions{} object, err := m.client.GetObject(m.bucket, objectName, opts) if err != nil { return nil, err } return object, nil } // Open open a file func (m *MinioStorage) Open(path string) (io.ReadCloser, error) { var opts = minio.GetObjectOptions{} object, err := m.client.GetObject(m.bucket, m.buildMinioPath(path), opts) if err != nil { return nil, err } return object, nil } // Save save a file to minio func (m *MinioStorage) Save(path string, r io.Reader) (int64, error) { return m.client.PutObject(m.bucket, m.buildMinioPath(path), r, -1, minio.PutObjectOptions{ContentType: "application/octet-stream"}) } // Delete delete a file func (m *MinioStorage) Delete(path string) error { return m.client.RemoveObject(m.bucket, m.buildMinioPath(path)) } // Delete delete a file func (m *MinioStorage) DeleteDir(dir string) error { objectsCh := make(chan string) // Send object names that are needed to be removed to objectsCh go func() { defer close(objectsCh) // List all objects from a bucket-name with a matching prefix. for object := range m.client.ListObjects(m.bucket, dir, true, nil) { if object.Err != nil { log.Error("ListObjects failed:%v", object.Err) } objectsCh <- object.Key } }() m.client.RemoveObjects(m.bucket, objectsCh) return nil } //Get Presigned URL for get object func (m *MinioStorage) PresignedGetURL(path string, fileName string) (string, error) { // Set request parameters for content-disposition. reqParams := make(url.Values) reqParams.Set("response-content-disposition", "attachment; filename=\""+fileName+"\"") var preURL *url.URL preURL, err := m.client.PresignedGetObject(m.bucket, path, PresignedGetUrlExpireTime, reqParams) if err != nil { return "", err } return preURL.String(), nil } //Get Presigned URL for put object func (m *MinioStorage) PresignedPutURL(path string) (string, error) { var preURL *url.URL preURL, err := m.client.PresignedPutObject(m.bucket, m.buildMinioPath(path), PresignedPutUrlExpireTime) if err != nil { return "", err } return preURL.String(), nil } //check if has the object func (m *MinioStorage) HasObject(path string) (bool, error) { hasObject := false // Create a done channel to control 'ListObjects' go routine. doneCh := make(chan struct{}) // Indicate to our routine to exit cleanly upon return. defer close(doneCh) //objectCh := m.client.ListObjects(m.bucket, m.buildMinioPath(path), false, doneCh) objectCh := m.client.ListObjects(m.bucket, path, false, doneCh) for object := range objectCh { if object.Err != nil { return hasObject, object.Err } hasObject = true } return hasObject, nil } //upload object func (m *MinioStorage) UploadObject(fileName, filePath string) error { _, err := m.client.FPutObject(m.bucket, fileName, filePath, minio.PutObjectOptions{}) return err } func GetMinioPath(jobName, suffixPath string) string { return setting.Attachment.Minio.RealPath + setting.Attachment.Minio.Bucket + "/" + setting.CBCodePathPrefix + jobName + suffixPath }