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.mzph.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…

无涯教程-Perl - tell函数

描述 此函数返回指定FILEHANDLE中读取指针的当前位置(以字节为单位)。如果省略FILEHANDLE,则它将返回上次访问的文件中的位置。 语法 以下是此函数的简单语法- tell FILEHANDLEtell返回值 此函数以字节为单位返回当前文件位置。 例 以下是显示其基本用法的示例代码,要检…

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

火柴拼正方形 leetcode473 火柴拼正方形题目描述回溯算法 上期经典算法 leetcode473 火柴拼正方形 难度 - 中等 原题链接 - leetcode473 火柴拼正方形 题目描述 你将得到一个整数数组 matchsticks &#xff0c;其中 matchsticks[i] 是第 i 个火柴棒的长度。你要用 所有的火柴棍…

微服务—Eureka注册中心

eureka相当于是一个公司的管理人事HR,各部门之间如果有合作时&#xff0c;由HR进行人员的分配以及调度&#xff0c;具体选哪个人&#xff0c;全凭HR的心情&#xff0c;如果你这个部门存在没有意义&#xff0c;直接把你这个部门撤销&#xff0c;全体人员裁掉&#xff0c;所以不想…

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…

教育行业软文怎么写,媒介盒子无偿分享

随着产业升级和技术变革、信息的智能化、数字化发展&#xff0c;也为教育行业带来了新的增长点&#xff0c;在线教育课程类型丰富多元&#xff0c;新课程不断涌现。在激烈的市场竞争环境下&#xff0c;教育机构如何根据市场实行差异化战略并加强自身品牌建成为挑战。 如今&…

微服务-Ribbon(负载均衡)

负载均衡的面对多个相同的服务的时候&#xff0c;我们选择一定的策略去选择一个服务进行 负载均衡流程 Ribbon结构组成 负载均衡策略 RoundRobinRule&#xff1a;简单的轮询服务列表来选择服务器AvailabilityFilteringRule 对两种情况服务器进行忽略&#xff1a; 1.在默认情…

315官方点赞!多燕瘦或将成酵素选购唯一标准

食用酵素及其衍生产品&#xff0c;是近年来国内主流电商平台的主要增长类目之一。在全球范围内&#xff0c;酵素的流行由来已久&#xff0c;其中在日本、北美、欧洲等发达国家和地区尤为风靡。据不完全统计&#xff1a;欧洲酵素市场规模约占全球酵素市场份额的40%以上&#xff…

自定义Android滑块拼图验证控件

自定义Android滑块拼图验证控件 拼图认证视图默认策略工具类参考 1、继承自AppCompatImageView&#xff0c;兼容ImageView的scaleType设置&#xff0c;可设置离线/在线图片。 2、通过设置滑块模型&#xff08;透明背景的图形块&#xff09;设置滑块&#xff08;和缺省块&#x…

【HarmonyOS北向开发】-01 HarmonyOS概述

飞书原文链接-【HarmonyOS北向开发】-01 HarmonyOS概述https://fvcs2dhq8qs.feishu.cn/docx/TDf2d2KMaoPSUUxnvg2cASDdnCe?fromfrom_copylink

Leetcode-每日一题【剑指 Offer 20. 表示数值的字符串】

题目 请实现一个函数用来判断字符串是否表示数值&#xff08;包括整数和小数&#xff09;。 数值&#xff08;按顺序&#xff09;可以分成以下几个部分&#xff1a; 若干空格一个 小数 或者 整数&#xff08;可选&#xff09;一个 e 或 E &#xff0c;后面跟着一个 整数若干空…

Linux NTP原理及配置使用

一、NTP简介 1.NTP简介 NTP&#xff08;Network Time Protocol&#xff0c;网络时间协议&#xff09;是用来使网络中的各个计算机时间同步的一种协议。它的用途是把计算机的时钟同步到世界协调时UTC&#xff0c;其精度在局域网内可达0.1ms&#xff0c;在互联网上绝大多数的…

CSS自学框架之动画

这一节&#xff0c;自学CSS动画。主要学习了淡入淡出、淡入缩放、缩放、移动、旋转动画效果。先看一下成果。 优雅的过渡动画&#xff0c;为你的页面添加另一份趣味&#xff01; 在你的选择器里插入 animation 属性&#xff0c;并添加框架内置的 keyframes 即可实现&#xff0…