Golang使用MinIO

最近在使用Golang做了一個網盤項目(學習),文件存儲一直保存在本地(各廠商提供的oss貴),所以就在思考怎么來處理這些文件,類似的方案很對hdfs、fastdfs,但這其中MinIO是最近幾年比較火熱的一個項目,所以嘗試使用這個試一試。

1、MinIO的安裝

MinIO的安裝特別簡單,大家可以前去官網按照步驟完成,注意以下幾點即可

  • 注意你服務器是amd還是arm架構
  • 注意你自己的網絡
  • 確保你按照官網的命令開啟了minio服務
minio server ~/minio --console-address :9090

目前不要隨便亂修改,按照原始的方案

2、創建Golang項目

mkdir minio-api
cd minio-api
go mod init v1

3、Goland打開項目構建一個上傳文件demo

創建一個main.go,在這個函數中我們首先創建一個用于初始化MinIOClient的函數,該函數細節如下:

func InitMinioClient() *minio.Client {// 基本的配置信息endpoint := "172.16.59.130:9000"accessKeyID := "IdoSKNGz7evlQXVVqGJF"secretAccessKey := "s4hnwC9yWOsU8TTmODFcMcw0TdExa4GsTpGzibEc"// 初始化一個minio客戶端對象minioClient, err := minio.New(endpoint, &minio.Options{Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),})if err != nil {log.Fatalf("初始化MinioClient錯誤:%s", err.Error())}return minioClient
}

有幾點基本的注意事項,首先是基本的配置信息你需要更改為你自己的,一般端口都為9000(注意不是9090),針對這個地方的accessKeyID和secretAccessKey的創建如下圖:
在這里插入圖片描述
然后我們構建一個main函數,在這個main函數中,我們首先調用初始化客戶端的函數InitMinioClient,然后我們在main實現一個簡單的上傳文件的demo

func main() {// 創建客戶端minioClient := InitMinioClient()// bucket名稱bucketName := "mypic"ctx := context.Background()// 創建這個bucketerr := minioClient.MakeBucket(ctx, bucketName, minio.MakeBucketOptions{})if err != nil {// 檢測這個bucket是否已經存在exists, errBucketExists := minioClient.BucketExists(ctx, bucketName)if errBucketExists == nil && exists {log.Printf("We already own %s\n", bucketName)} else {log.Fatalln(err)}} else {log.Printf("Successfully created %s\n", bucketName)}// 需要上傳文件的基本信息objectName := "頭像.jpg"filePath := "image"contentType := "multipart/form-data"fPath := filepath.Join(filePath, objectName)fileInfo, err := os.Stat(fPath)if err == os.ErrNotExist {log.Printf("%s目標文件不存在", fPath)}f, err := os.Open(fPath)if err != nil {return}uploadInfo, err := minioClient.PutObject(ctx, bucketName, objectName, f, fileInfo.Size(), minio.PutObjectOptions{ContentType: contentType})if err != nil {log.Fatalln(err)}log.Printf("Successfully uploaded %s of size %d\n", objectName, uploadInfo.Size)
}

在這里插入圖片描述
上傳文件即成功,然后打開網頁頁面刷新,出現下面頁面即可,同時你打開你的服務器可以發現你上傳的文件。
在這里插入圖片描述
觀察上面的代碼,至少包括三個API,包括創建Bucket、檢測Bucket是否存在、上傳文件到指定的Bucket。為了更好的研究這些API,現在我從Bucket開始研究一下常用的API。

4.創建客戶端對象

API接口

New(endpoint string, opts *Options) (*Client, error)

初步觀察這個接口,在結合我們上面的示例,可以發現:

  • endpoint:目前存儲地址(127.0.0.1:9000),當然這里需要根據自身開啟的minio服務所在的位置和服務端口。
  • opts:這個是一個minio.Options對象,然后我們來具體了解一哈這個對象有哪些常用的屬性(注意這些屬性的類):
    • Creds:存儲的一個身份信任
    • Secure:API的請求方式(HTTP/HTTPS)
  • 完整的一個創建Client的代碼(可以根據自身需要再改造)
func InitMinioClient() *minio.Client {// 基本的配置信息endpoint := "172.16.59.130:9000"accessKeyID := "IdoSKNGz7evlQXVVqGJF"secretAccessKey := "s4hnwC9yWOsU8TTmODFcMcw0TdExa4GsTpGzibEc"// 初始化一個minio客戶端對象minioClient, err := minio.New(endpoint, &minio.Options{Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),})if err != nil {log.Fatalf("初始化MinioClient錯誤:%s", err.Error())}return minioClient
}

你自己可以嘗試:創建一個bucket01和bucket02

5.Bucket的基本操作

5.1 創建Bucket

MakeBucket(ctx context.Context, bucketName string, opts MakeBucketOptions)

前兩個參數不作過多的介紹,非常的清晰(所謂的BuketName,你可以簡單理解為就是你文件保存的文件夾),在這里我們詳細得介紹一哈MakeBucketOptions(minio.MakeBucketOptions),其主要是用于指定Bucket的一些選項比如說Region(這個地方的Region表示在哪里創建你的Bucket,默認為us-east-1,其它的一些選項可以參考官網文檔,在這里需要注意,如果你是自己服務器搭建,而不是使用他所提供的存儲服務,其實你是不用指定這個,就默認值問題也不大),一個完整的示例如下(確保你事先已經有這樣的minioClient):

err = minioClient.MakeBucket(context.Background(),minio.MakeBucketOptions{Region: "us-east-1", ObjectLocking: true})
if err != nil {fmt.Println(err)return
}
fmt.Println("Successfully created mybucket.")

5.2 展示Bucket

ListBuckets(ctx context.Context) ([]BucketInfo, error)

這個接口非常明了,返回的是一個BucketInfo的slice,在這里我們可以遍歷該元素然后獲取到對應的BucketInfo對象,直接看下面的demo:

func ListBuckets(minioClient *minio.Client) {bucketInfos, err := minioClient.ListBuckets(context.Background())if err != nil {fmt.Println("List Buckets err:", err.Error())return}for index, bucketInfo := range bucketInfos {fmt.Printf("List Bucket No {%d}----filename{%s}-----createTime{%s}\n", index+1, bucketInfo.Name, bucketInfo.CreationDate.Format("2006-01-02 15:04:05"))}
}

輸出為:
List Bucket No {1}----filename{bucket01}-----createTime{2023-08-18 04:03:18}
List Bucket No {2}----filename{bucket02}-----createTime{2023-08-18 03:54:42}
注意:bucket是不支持修改名稱的,如果你要修改名稱,一般是新建一個bucket然后講原來需要改名的bucket的內容移到新建的一個bucket。

5.3 檢測Bucket

BucketExists(ctx context.Context, bucketName string) (found bool, err error)

檢查Bucket是否存在,查看下面demo:

func CheckBuckets(minioClient *minio.Client) {bucketName01 := "bucket01"bucketName02 := "bucket03"isExist, err := minioClient.BucketExists(context.Background(), bucketName01)if err != nil {fmt.Printf("Check %s err:%s", bucketName01, err.Error())return}if isExist {fmt.Printf("%s exists!\n", bucketName01)} else {fmt.Printf("%s not exists!\n", bucketName01)}isExist, err = minioClient.BucketExists(context.Background(), bucketName02)if err != nil {fmt.Printf("Check %s err:%s", bucketName02, err.Error())return}if isExist {fmt.Printf("%s exists!\n", bucketName02)} else {fmt.Printf("%s not exists!\n", bucketName02)}
}

輸出:
bucket01 exists!
bucket03 not exists!

5.4 刪除Bucket

RemoveBucket(ctx context.Context, bucketName string) error

刪除名稱為bucketName的bucket,刪除之前建議先檢查該bucket是否存在,查看下面的demo:

func RemoveBucket(minioClient *minio.Client) {bucketName01 := "bucket01"isExist, err := minioClient.BucketExists(context.Background(), bucketName01)if err != nil {fmt.Printf("Check %s err:%s", bucketName01, err.Error())return}if isExist {fmt.Printf("%s exists! Start delete....\n", bucketName01)// 開始刪除邏輯err = minioClient.RemoveBucket(context.Background(), bucketName01)if err != nil {fmt.Printf("Fail to remove %s:%s\n", bucketName01, err.Error())return}fmt.Printf("Success to remove %s\n", bucketName01)} else {fmt.Printf("%s not exists!\n", bucketName01)}
}

輸出為:
bucket01 exists! Start delete…
Success to remove bucket01

5.6 展示對象Object

ListObjects(ctx context.Context, bucketName string, opts ListObjectsOptions) <-chan ObjectInfo

展示一個Object中的所有對象,注意返回的是一個channel。在查看下面的demo,請參考之前的內容先上傳一些對象:

func ListObjects(minioClient *minio.Client) {ctx, cancel := context.WithCancel(context.Background())defer cancel()bucketName := "bucket02"opts := minio.ListObjectsOptions{Prefix:    "頭",Recursive: true,}objectCh := minioClient.ListObjects(ctx, bucketName, opts)for obj := range objectCh {fmt.Printf("Name:%s\tSize:%d\tMD5:%s\tModifiedTime:%s\n",obj.Key, obj.Size, obj.ETag, obj.LastModified.Format("2006-01-02 03:04:05"))}
}

輸出為:
Name:頭像.jpg Size:739938 MD5:10bf76e379cd8f381791c6924f33dcd6 ModifiedTime:2023-08-18 05:22:34
注意,在這里Prefix就是所有Object的的前綴。

針對Bucket的操作就到這里,但是minio還提供其余操作,比喻設置tag等等,上面所列舉的是常用的一些操作

6. Object操作

6.1 獲取Object

GetObject(ctx context.Context, bucketName, objectName string, opts GetObjectOptions) (*Object, error)

返回一個數據流對象,注意是一個數據流,所以要寫入到一個具體的對象中,詳見下面demo的使用:

func GetObjects(minioClient *minio.Client) {bucketName := "bucket02"objectName := "頭像.jpg"object, err := minioClient.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})if err != nil {fmt.Println(err)return}defer func(object *minio.Object) {err := object.Close()if err != nil {fmt.Println(err)return}}(object)localFile, err := os.Create("image/local-file.jpg")if err != nil {fmt.Println(err)return}defer func(localFile *os.File) {err := localFile.Close()if err != nil {return}}(localFile)if _, err = io.Copy(localFile, object); err != nil {fmt.Println(err)return}
}

注意這個地方文件的寫入。

6.2 放入Object

PutObject(ctx context.Context, bucketName, objectName string, reader io.Reader, objectSize int64,opts PutObjectOptions) (info UploadInfo, err error)

將一個文件放入到bucket中,詳細見下面的操作,其實之前我們的demo中已經有了這個操作:

func PutObjects(minioClient *minio.Client) {bucketName := "bucket02"// 檢查bucket是否存在isExist, err := minioClient.BucketExists(context.Background(), bucketName)if err != nil {fmt.Printf("Check %s err:%s", bucketName, err.Error())return}if !isExist {fmt.Printf("%s not exists!\n", bucketName)}// 對象信息objectName := "頭像.jpg"filePath := "image"contentType := "multipart/form-data"fPath := filepath.Join(filePath, objectName)// 讀取對象流fileInfo, err := os.Stat(fPath)if err == os.ErrNotExist {log.Printf("%s目標文件不存在", fPath)}f, err := os.Open(fPath)if err != nil {log.Printf("%s打開目標文件", fPath)return}// 上傳文件uploadInfo, err := minioClient.PutObject(context.Background(), bucketName,objectName, f, fileInfo.Size(),minio.PutObjectOptions{ContentType: contentType})if err != nil {log.Fatalln(err)return}log.Printf("Successfully uploaded %s of size %d\n", objectName, uploadInfo.Size)
}

最后這個文件會被放在你minio服務器上的minio下,你可以去該文件下查看在這里插入圖片描述

6.3 復制Objects

CopyObject(ctx context.Context, dst CopyDestOptions, src CopySrcOptions) (UploadInfo, error)

將一個文件復制到另一個bucket中,詳見下面demo:

func CopyObjects(minioClient *minio.Client) {// Source objectsrcOpts := minio.CopySrcOptions{Bucket: "bucket02",Object: "頭像.jpg",}// Destination objectdstOpts := minio.CopyDestOptions{Bucket: "bucket01",Object: "圖片.jpg",}// copyuploadInfo, err := minioClient.CopyObject(context.Background(), dstOpts, srcOpts)if err != nil {fmt.Println(err)return}fmt.Println("Successfully copied object:", uploadInfo)
}

輸出為:
Successfully copied object: {bucket01 圖片.jpg 10bf76e379cd8f381791c6924f33dcd6 0 2023-08-18 08:144.773 +0000 UTC 0001-01-01 00:00:00 +0000 UTC }

6.4 狀態Objects

StatObject(ctx context.Context, bucketName, objectName string, opts StatObjectOptions) (ObjectInfo, error)

返回一個object的元數據,demo如下:

func StateObjects(minioClient *minio.Client) {ObjInfo, err := minioClient.StatObject(context.Background(), "bucket02", "頭像.jpg", minio.StatObjectOptions{})if err != nil {fmt.Println(err)return}fmt.Printf("LastModified:%s\tETag:%s\tContentType:%s\tSize:%d\n",ObjInfo.LastModified.Format("2006-01-02 03:04:05"),ObjInfo.ETag, ObjInfo.ContentType, ObjInfo.Size)
}

輸出為:
LastModified:2023-08-18 05:22:34 ETag:10bf76e379cd8f381791c6924f33dcd6 ContentType:multipart/form-data Size:739938

6.5 刪除Object

RemoveObject(ctx context.Context, bucketName, objectName string, opts minio.RemoveObjectOptions) error

刪除一個object,注意下面demo:

func RemoveObject(minioClient *minio.Client) {opts := minio.RemoveObjectOptions{}err := minioClient.RemoveObject(context.Background(), "bucket01", "圖片.jpg", opts)if err != nil {fmt.Println(err)return}
}

6.6 批量刪除Object

RemoveObjects(ctx context.Context, bucketName string, objectsCh <-chan ObjectInfo, opts RemoveObjectsOptions) <-chan RemoveObjectError

批量刪除一個bucket中的object,關鍵就是構造這里的objectsCh,詳細見demo:

func RemoveObjects(minioClient *minio.Client) {objectsCh := make(chan minio.ObjectInfo)// 注意一般不要自己來構造,直接選擇從bucket中查詢,查詢到的對象放入objectsChfor object := range minioClient.ListObjects(context.Background(), "bucket02", minio.ListObjectsOptions{}) {if object.Key == "頭像.jpg" {objectsCh <- object}}defer close(objectsCh)// 刪除for rErr := range minioClient.RemoveObjects(context.Background(), "bucket02", objectsCh, minio.RemoveObjectsOptions{}) {fmt.Println("Delete err:", rErr.Err.Error())}
}

6.7 上傳大對象Object

FPutObject(ctx context.Context, bucketName, objectName, filePath, opts PutObjectOptions) (info UploadInfo, err error)

該demo如下:

func UploadLargeFileObjects(minioClient *minio.Client) {uploadInfo, err := minioClient.FPutObject(context.Background(), "bucket02", "test.csv", "data", minio.PutObjectOptions{ContentType: "application/csv",})if err != nil {fmt.Println(err)return}fmt.Println("Successfully uploaded object: ", uploadInfo)
}

在MinIO中,PutObject和FPutObject主要有以下幾點區別:

  • 操作對象方式不同:
    • PutObject上傳對象采用單個對象上傳方式,一次請求上傳一個對象。
    • FPutObject上傳對象采用分片上傳方式,可以在多個請求中上傳同一個對象。
  • 適用場景不同:
    • PutObject適用于小對象的上傳。
    • FPutObject適用于大對象的上傳,可以支持超過5GB的對象上傳。
  • 上傳方式不同:
    • PutObject直接上傳對象。
    • FPutObject需要首先調用InitiateMultipartUpload初始化分片上傳,然后調用UploadPart上傳每個分片,最后調用CompleteMultipartUpload完成上傳。
  • 錯誤處理不同:
    • PutObject上傳錯誤需要重新上傳整個對象。
    • FPutObject上傳錯誤只需要重新上傳出錯的分片。
  • 上傳事務不同:
    • PutObject上傳具有原子性,一個對象要么完全上傳,要么失敗。
    • FPutObject上傳可以暫停和恢復,但多個分片上傳完成后才視為成功。
      所以簡單來說,對于小對象可以直接使用PutObject,對于大對象建議使用FPutObject分片上傳。

6.8 下載大對象

FGetObject(ctx context.Context, bucketName, objectName, filePath string, opts GetObjectOptions) error

下載大文件,詳見下面demo:

func DownloadLargeFileObjects(minioClient *minio.Client) {err := minioClient.FGetObject(context.Background(), "bucket02", "test.csv", "/tmp/myobject", minio.GetObjectOptions{})if err != nil {fmt.Println(err)return}
}

基本上的操作就是這樣,當然這里還可以需要設置一些策略設置就不在這里詳講了,可以去官網查看.

完整代碼:

package mainimport ("context""fmt""github.com/minio/minio-go/v7""github.com/minio/minio-go/v7/pkg/credentials""io""log""os""path/filepath"
)func InitMinioClient() *minio.Client {// 基本的配置信息endpoint := "172.16.59.129:9000"accessKeyID := "6b6U1MlseU8h9dDkACNj"secretAccessKey := "Hf6NSEHjXwHiApoymYR0yktNUkbZwt3MYYRmXOgT"// 初始化一個minio客戶端對象minioClient, err := minio.New(endpoint, &minio.Options{Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),})if err != nil {log.Fatalf("初始化MinioClient錯誤:%s", err.Error())}return minioClient
}func main() {// 創建客戶端minioClient := InitMinioClient()// Make a new bucket called mymusic.// bucketName := "mypic"//ctx := context.Background()//err := minioClient.MakeBucket(ctx, bucketName, minio.MakeBucketOptions{})//if err != nil {//	// Check to see if we already own this bucket (which happens if you run this twice)//	exists, errBucketExists := minioClient.BucketExists(ctx, bucketName)//	if errBucketExists == nil && exists {//		log.Printf("We already own %s\n", bucketName)//	} else {//		log.Fatalln(err)//	}//} else {//	log.Printf("Successfully created %s\n", bucketName)//}// Upload the zip file//objectName := "頭像.jpg"//filePath := "image"//contentType := "multipart/form-data"//fPath := filepath.Join(filePath, objectName)//fileInfo, err := os.Stat(fPath)//if err == os.ErrNotExist {//	log.Printf("%s目標文件不存在", fPath)//}////f, err := os.Open(fPath)//if err != nil {//	return//}////uploadInfo, err := minioClient.PutObject(ctx, bucketName, objectName, f, fileInfo.Size(), minio.PutObjectOptions{ContentType: contentType})//if err != nil {//	log.Fatalln(err)//}////log.Printf("Successfully uploaded %s of size %d\n", objectName, uploadInfo.Size)// ListBuckets(minioClient)//CheckBuckets(minioClient)//RemoveBucket(minioClient)//UploadObjToBucket(minioClient)//ListObjects(minioClient)//GetObjects(minioClient)//CopyObjects(minioClient)//StateObjects(minioClient)//RemoveObject(minioClient)RemoveObjects(minioClient)
}func ListBuckets(minioClient *minio.Client) {bucketInfos, err := minioClient.ListBuckets(context.Background())if err != nil {fmt.Println("List Buckets err:", err.Error())return}for index, bucketInfo := range bucketInfos {fmt.Printf("List Bucket No {%d}----filename{%s}-----createTime{%s}\n", index+1, bucketInfo.Name, bucketInfo.CreationDate.Format("2006-01-02 15:04:05"))}
}func CheckBuckets(minioClient *minio.Client) {bucketName01 := "bucket01"bucketName02 := "bucket03"isExist, err := minioClient.BucketExists(context.Background(), bucketName01)if err != nil {fmt.Printf("Check %s err:%s", bucketName01, err.Error())return}if isExist {fmt.Printf("%s exists!\n", bucketName01)} else {fmt.Printf("%s not exists!\n", bucketName01)}isExist, err = minioClient.BucketExists(context.Background(), bucketName02)if err != nil {fmt.Printf("Check %s err:%s", bucketName02, err.Error())return}if isExist {fmt.Printf("%s exists!\n", bucketName02)} else {fmt.Printf("%s not exists!\n", bucketName02)}
}func RemoveBucket(minioClient *minio.Client) {bucketName01 := "bucket01"isExist, err := minioClient.BucketExists(context.Background(), bucketName01)if err != nil {fmt.Printf("Check %s err:%s", bucketName01, err.Error())return}if isExist {fmt.Printf("%s exists! Start delete....\n", bucketName01)// 開始刪除邏輯err = minioClient.RemoveBucket(context.Background(), bucketName01)if err != nil {fmt.Printf("Fail to remove %s:%s\n", bucketName01, err.Error())return}fmt.Printf("Success to remove %s\n", bucketName01)} else {fmt.Printf("%s not exists!\n", bucketName01)}
}func ListObjects(minioClient *minio.Client) {ctx, cancel := context.WithCancel(context.Background())defer cancel()bucketName := "bucket02"opts := minio.ListObjectsOptions{Prefix:    "頭",Recursive: true,}objectCh := minioClient.ListObjects(ctx, bucketName, opts)for obj := range objectCh {fmt.Printf("Name:%s\tSize:%d\tMD5:%s\tModifiedTime:%s\n",obj.Key, obj.Size, obj.ETag, obj.LastModified.Format("2006-01-02 03:04:05"))}
}func GetObjects(minioClient *minio.Client) {bucketName := "bucket02"objectName := "頭像.jpg"object, err := minioClient.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})if err != nil {fmt.Println(err)return}defer func(object *minio.Object) {err := object.Close()if err != nil {fmt.Println(err)return}}(object)localFile, err := os.Create("image/local-file.jpg")if err != nil {fmt.Println(err)return}defer func(localFile *os.File) {err := localFile.Close()if err != nil {return}}(localFile)if _, err = io.Copy(localFile, object); err != nil {fmt.Println(err)return}
}func PutObjects(minioClient *minio.Client) {bucketName := "bucket02"// 檢查bucket是否存在isExist, err := minioClient.BucketExists(context.Background(), bucketName)if err != nil {fmt.Printf("Check %s err:%s", bucketName, err.Error())return}if !isExist {fmt.Printf("%s not exists!\n", bucketName)}// 對象信息objectName := "頭像.jpg"filePath := "image"contentType := "multipart/form-data"fPath := filepath.Join(filePath, objectName)// 讀取對象流fileInfo, err := os.Stat(fPath)if err == os.ErrNotExist {log.Printf("%s目標文件不存在", fPath)}f, err := os.Open(fPath)if err != nil {log.Printf("%s打開目標文件", fPath)return}// 上傳文件uploadInfo, err := minioClient.PutObject(context.Background(), bucketName,objectName, f, fileInfo.Size(),minio.PutObjectOptions{ContentType: contentType})if err != nil {log.Fatalln(err)return}log.Printf("Successfully uploaded %s of size %d\n", objectName, uploadInfo.Size)
}func CopyObjects(minioClient *minio.Client) {// Source objectsrcOpts := minio.CopySrcOptions{Bucket: "bucket02",Object: "頭像.jpg",}// Destination objectdstOpts := minio.CopyDestOptions{Bucket: "bucket01",Object: "圖片.jpg",}// copyuploadInfo, err := minioClient.CopyObject(context.Background(), dstOpts, srcOpts)if err != nil {fmt.Println(err)return}fmt.Println("Successfully copied object:", uploadInfo)
}func StateObjects(minioClient *minio.Client) {ObjInfo, err := minioClient.StatObject(context.Background(), "bucket02", "頭像.jpg", minio.StatObjectOptions{})if err != nil {fmt.Println(err)return}fmt.Printf("LastModified:%s\tETag:%s\tContentType:%s\tSize:%d\n",ObjInfo.LastModified.Format("2006-01-02 03:04:05"),ObjInfo.ETag, ObjInfo.ContentType, ObjInfo.Size)
}func RemoveObject(minioClient *minio.Client) {opts := minio.RemoveObjectOptions{}err := minioClient.RemoveObject(context.Background(), "bucket01", "圖片.jpg", opts)if err != nil {fmt.Println(err)return}
}func RemoveObjects(minioClient *minio.Client) {objectsCh := make(chan minio.ObjectInfo)// 注意一般不要自己來構造,直接選擇從bucket中查詢,查詢到的對象放入objectsChfor object := range minioClient.ListObjects(context.Background(), "bucket02", minio.ListObjectsOptions{}) {if object.Key == "頭像.jpg" {objectsCh <- object}}defer close(objectsCh)// 刪除for rErr := range minioClient.RemoveObjects(context.Background(), "bucket02", objectsCh, minio.RemoveObjectsOptions{}) {fmt.Println("Delete err:", rErr.Err.Error())}
}func UploadLargeFileObjects(minioClient *minio.Client) {uploadInfo, err := minioClient.FPutObject(context.Background(), "bucket02", "test.csv", "data", minio.PutObjectOptions{ContentType: "application/csv",})if err != nil {fmt.Println(err)return}fmt.Println("Successfully uploaded object: ", uploadInfo)
}func DownloadLargeFileObjects(minioClient *minio.Client) {err := minioClient.FGetObject(context.Background(), "bucket02", "test.csv", "/tmp/myobject", minio.GetObjectOptions{})if err != nil {fmt.Println(err)return}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/42010.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/42010.shtml
英文地址,請注明出處:http://en.pswp.cn/news/42010.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

生信豆芽菜-差異基因富集分析的圈圖

網址&#xff1a;http://www.sxdyc.com/visualsEnrichCirplot 1、數據準備 準備一個基因集的文件 2、選擇富集分析的數據庫&#xff0c;同時輸入展示top幾的條目&#xff0c;選擇顏色&#xff0c;如果是GO的話選擇三個顏色&#xff0c;如果是KEGG選擇一個&#xff0c;如果是G…

神經網絡論文研讀-多模態方向-綜述研讀(上)

翻譯以機翻為主 原文目錄 前言 圖1&#xff1a;LMU印章&#xff08;左&#xff09;風格轉移到梵高的向日葵繪畫&#xff08;中&#xff09;并與提示混合 - 梵高&#xff0c;向日葵 -通過CLIPVGAN&#xff08;右&#xff09;。在過去的幾年中&#xff0c;自然語言處理&#xff…

微信小程序實現拖拽的小球

目錄 前言 js 獲取微信小程序中獲取系統信息 觸摸移動事件的處理函數 觸摸結束事件的處理函數 用于監聽頁面滾動事件 全局參數 html CSS 前言 小程序開發提供了豐富的API和事件處理函數&#xff0c;使得開發者可以方便地實現各種交互功能。其中&#xff0c;拖拽功能…

無涯教程-Perl - tell函數

描述 此函數返回指定FILEHANDLE中讀取指針的當前位置(以字節為單位)。如果省略FILEHANDLE,則它將返回上次訪問的文件中的位置。 語法 以下是此函數的簡單語法- tell FILEHANDLEtell返回值 此函數以字節為單位返回當前文件位置。 例 以下是顯示其基本用法的示例代碼,要檢…

leetcode473. 火柴拼正方形(回溯算法-java)

火柴拼正方形 leetcode473 火柴拼正方形題目描述回溯算法 上期經典算法 leetcode473 火柴拼正方形 難度 - 中等 原題鏈接 - leetcode473 火柴拼正方形 題目描述 你將得到一個整數數組 matchsticks &#xff0c;其中 matchsticks[i] 是第 i 個火柴棒的長度。你要用 所有的火柴棍…

BC119 小樂樂與字符串

描述 在慶祝祖國母親70華誕之際&#xff0c;老師給小樂樂出了一個問題。大家都知道China的英文縮寫是CHN&#xff0c;那么給你一個字符串s&#xff0c;你需要做的是統計s中子序列“CHN”的個數。子序列的定義&#xff1a;存在任意下標a < b < c&#xff0c;那么“s[a]s[b…

微服務—Eureka注冊中心

eureka相當于是一個公司的管理人事HR,各部門之間如果有合作時&#xff0c;由HR進行人員的分配以及調度&#xff0c;具體選哪個人&#xff0c;全憑HR的心情&#xff0c;如果你這個部門存在沒有意義&#xff0c;直接把你這個部門撤銷&#xff0c;全體人員裁掉&#xff0c;所以不想…

計算機網絡筆記

TCP有連接可靠服務 TCP特點&#xff1a; 1.TCP是面向連接的傳輸層協議&#xff1b; 2.每條TCP連接只能有兩個端點&#xff0c;每條TCP連接是一對一的&#xff1b; 3.TCP提供可靠交付&#xff0c;保證傳送數據無差錯&#xff0c;不丟失&#xff0c;不重復且有序&#xff1b; 4.…

Android Studio瀑布流實現

效果&#xff1a; ImageDetail class package com.example.waterfallflow; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.widget.ImageView;public class ImageDetail extends Activity{Overrideprotected void …

DNNGP、DeepGS 和 DLGWAS模型構成對比

一、DNNGP DNNGP 是基于深度卷積神經網絡&#xff0c;這個結構包括一個輸入層&#xff0c;三個卷積層&#xff0c;一個批標準化層&#xff0c;兩個dropout層&#xff0c;一個平坦化層&#xff0c;一個 dense層。 dropout層&#xff1a;在神經網絡中,dropout層是一個非常有效的正…

信息與通信工程面試準備——數學知識|正態分布|中心極限定理

目錄 正態分布 正態分布的參數 正態分布的第一個參數是均值 正態分布的第二個參數是標準差SD 所有正態分布的共同特征 標準正態分布&#xff1a;正態分布的特例 中心極限定理 理解定義 示例# 1 示例# 2 知道樣本均值總是正態分布的實際含義是什么&#xff1f; 正態分…

Scala 如何調試隱式轉換--隱式轉換代碼的顯示展示

方法1 在需要隱式轉換的地方&#xff0c;把需要的參數顯示的寫出。 略方法2&#xff0c;查看編譯代碼 在terminal中 利用 scalac -Xprint:typer xxx.scala方法打印添加了隱式值的代碼示例。 對于復雜的工程來說&#xff0c;直接跑到terminal執行 scalac -Xprint:typer xxx.…

JVM——類文件結構

文章目錄 一 概述二 Class 文件結構總結2.1 魔數2.2 Class 文件版本2.3 常量池2.4 訪問標志2.5 當前類索引,父類索引與接口索引集合2.6 字段表集合2.7 方法表集合2.8 屬性表集合 一 概述 在 Java 中&#xff0c;JVM 可以理解的代碼就叫做字節碼&#xff08;即擴展名為 .class …

winform 封裝unity web player 用戶控件

環境&#xff1a; VS2015Unity 5.3.6f1 (64-bit) 目的&#xff1a; Unity官方提供的UnityWebPlayer控件在嵌入Winform時要求讀取的.unity3d文件路徑&#xff08;Src&#xff09;必須是絕對路徑&#xff0c;如果移動代碼到另一臺電腦&#xff0c;需要重新修改src。于是考慮使…

elementUI 的上傳組件<el-upload>,自定義上傳按鈕樣式

方法一&#xff1a; 原理&#xff1a;調用<el-upload>組件的方法喚起選擇文件事件 效果&#xff1a; 頁面代碼&#xff1a; 1、選擇圖片按鈕 <div class"flex_row_spacebetween btn" click"chooseImg"><span class"el-icon-plus ic…

matlab機器人工具箱基礎使用

資料&#xff1a;https://blog.csdn.net/huangjunsheng123/article/details/110630665 用vscode直接看工具箱api代碼比較方便&#xff0c;代碼說明很多 一、模型設置 1、基礎效果 %采用機器人工具箱進行正逆運動學驗證 a[0,-0.3,-0.3,0,0,0];%DH參數 d[0.05,0,0,0.06,0.05,…

教育行業軟文怎么寫,媒介盒子無償分享

隨著產業升級和技術變革、信息的智能化、數字化發展&#xff0c;也為教育行業帶來了新的增長點&#xff0c;在線教育課程類型豐富多元&#xff0c;新課程不斷涌現。在激烈的市場競爭環境下&#xff0c;教育機構如何根據市場實行差異化戰略并加強自身品牌建成為挑戰。 如今&…

微服務-Ribbon(負載均衡)

負載均衡的面對多個相同的服務的時候&#xff0c;我們選擇一定的策略去選擇一個服務進行 負載均衡流程 Ribbon結構組成 負載均衡策略 RoundRobinRule&#xff1a;簡單的輪詢服務列表來選擇服務器AvailabilityFilteringRule 對兩種情況服務器進行忽略&#xff1a; 1.在默認情…

Php“牽手”拼多多商品詳情頁數據采集方法,拼多多API接口申請指南

拼多多詳情接口 API 是開放平臺提供的一種 API 接口&#xff0c;它可以幫助開發者獲取商品的詳細信息&#xff0c;包括商品的標題、描述、圖片等信息。在電商平臺的開發中&#xff0c;詳情接口API是非常常用的 API&#xff0c;因此本文將詳細介紹詳情接口 API 的使用。 一、拼…

315官方點贊!多燕瘦或將成酵素選購唯一標準

食用酵素及其衍生產品&#xff0c;是近年來國內主流電商平臺的主要增長類目之一。在全球范圍內&#xff0c;酵素的流行由來已久&#xff0c;其中在日本、北美、歐洲等發達國家和地區尤為風靡。據不完全統計&#xff1a;歐洲酵素市場規模約占全球酵素市場份額的40%以上&#xff…