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()
	// 创建这个bucket
	err := 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 object
	srcOpts := minio.CopySrcOptions{
		Bucket: "bucket02",
		Object: "头像.jpg",
	}

	// Destination object
	dstOpts := minio.CopyDestOptions{
		Bucket: "bucket01",
		Object: "图片.jpg",
	}

	// copy
	uploadInfo, 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中查询,查询到的对象放入objectsCh
	for 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 main

import (
	"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 object
	srcOpts := minio.CopySrcOptions{
		Bucket: "bucket02",
		Object: "头像.jpg",
	}

	// Destination object
	dstOpts := minio.CopyDestOptions{
		Bucket: "bucket01",
		Object: "图片.jpg",
	}

	// copy
	uploadInfo, 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中查询,查询到的对象放入objectsCh
	for 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.mfbz.cn/a/80127.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Django】Task1安装python环境及运行项目

【Django】Task1安装python环境及运行项目 写在最前 8月份Datawhale组队学习&#xff0c;在这个群除我佬的时代&#xff0c;写一下blog记录学习过程。 参考资源&#xff1a; 学习项目github&#xff1a;https://github.com/Joe-2002/sweettalk-django4.2 队长博客&#xff1a…

windows权限维持—SSPHOOKDSRMSIDhistorySkeletonKey

windows权限维持—SSP&HOOK&DSRM&SIDhistory&SkeletonKey 1. 权限维持介绍1.1. 其他 2. 基于验证DLL加载—SPP2.1. 操作演示—临时生效2.1.1. 执行命令2.1.2. 切换用户 2.2. 操作演示—永久生效2.2.1. 上传文件2.2.2. 执行命令2.2.3. 重启生效 2.3. 总结 3. 基…

k8s的pv和pvc创建

//NFS使用PV和PVC 1、配置nfs存储 2、定义PV 实现 下图的pv和pvc测试 pv的定义 这里定义5个PV&#xff0c;并且定义挂载的路径以及访问模式&#xff0c;还有PV划分的大小 vim /pv.yamlapiVersion: v1 kind: PersistentVolume metadata:name: pv001 spec:capacity:storage: …

私密数据采集:隧道爬虫IP技术的保密性能力探究

作为一名专业的爬虫程序员&#xff0c;今天要和大家分享一个关键的技术&#xff0c;它能够为私密数据采集提供保密性能力——隧道爬虫IP技术。如果你在进行敏感数据采集任务时需要保护数据的私密性&#xff0c;那么这项技术将是你的守护神。 在进行私密数据采集任务时&#xff…

【制作npm包4】api-extractor 学习

制作npm包目录 本文是系列文章&#xff0c; 作者一个橙子pro&#xff0c;本系列文章大纲如下。转载或者商业修改必须注明文章出处 一、申请npm账号、个人包和组织包区别 二、了解 package.json 相关配置 三、 了解 tsconfig.json 相关配置 四、 api-extractor 学习 五、npm包…

Fluent-MyBatis

Fluent-MyBatis Fluent-MyBatis 简介 何为 Fluent Mybatis&#xff1f; Fluent Mybatis, 是一款 Mybatis 语法增强框架, 综合了 Mybatis Plus, Dynamic SQL, JPA 等框架特性和优点 Fluent-MyBatis 开源地址 Fluent-MyBatis 原理 Fluent-MyBatis 原理是利用 annotation pro…

【IMX6ULL驱动开发学习】08.马达驱动实战:驱动编写、手动注册平台设备和设备树添加节点信息

目录 一、使用设备树 1.1 修改设备树流程 二、手动创建平台设备 三、总结&#xff08;附驱动程序&#xff09; 前情提要&#xff1a;​​​​​​​【IMX6ULL驱动开发学习】07.驱动程序分离的思想之平台总线设备驱动模型和设备树_阿龙还在写代码的博客-CSDN博客 手动注册…

爬虫逆向实战(五)--猿人学第三题

一、数据接口分析 主页地址&#xff1a;猿人学第三题 1、抓包 通过抓包可以发现数据接口是api/match/3 2、判断是否有加密参数 请求参数是否加密&#xff1f; 无请求头是否加密&#xff1f; 无响应是否加密&#xff1f; 无cookie是否加密&#xff1f; 无 二、发送请求 …

什么是闭包(closure)?为什么它在JavaScript中很有用?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 闭包&#xff08;Closure&#xff09;是什么&#xff1f;⭐ 闭包的用处⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&…

基于安防监控EasyCVR视频汇聚融合技术的运输管理系统的分析

一、项目背景 近年来&#xff0c;随着物流行业迅速发展&#xff0c;物流运输费用高、运输过程不透明、货损货差率高、供应链协同能力差等问题不断涌现&#xff0c;严重影响了物流作业效率&#xff0c;市场对于运输管理数字化需求愈发迫切。当前运输行业存在的难题如下&#xf…

架构师必备--高可用高性能分布式数据库Tidb安装部署实践

本文针对分布式、高可用的tidb数据库&#xff0c;从搭建实际生产环境的集群服务&#xff0c;介绍下tidb的安装流程、安装前的环境检测和系统优化、服务访问等方面介绍下具体的流程&#xff0c;希望对大家熟悉和了解tidb数据库有所帮助&#xff0c;减少不必要的弯路。 1.概述 …

基于WebSocket的在线文字聊天室

与Ajax不同&#xff0c;WebSocket可以使服务端主动向客户发送响应&#xff0c;本案例就是基于WebSocket的一个在线聊天室&#xff0c;不过功能比较简单&#xff0c;只能满足文字交流。演示如下。 案例学习于b站up主&#xff0c;链接 。这位up主讲的非常清楚&#xff0c;值得去学…

(黑客)自学

一、前言&#xff1a; 1.这是一条坚持的道路,三分钟的热情可以放弃往下看了. 2.多练多想,不要离开了教程什么都不会了.最好看完教程自己独立完成技术方面的开发. 3.有时多 google,baidu,我们往往都遇不到好心的大神,谁会无聊天天给你做解答. 4.遇到实在搞不懂的,可以先放放,以…

Java多态详解(1)

多态 多态的概念 所谓多态&#xff0c;通俗地讲&#xff0c;就是多种形态&#xff0c;具体点就是去完成某个行为&#xff0c;当不同的对象去完成时会产生出不同的状态。 比如&#xff1a; 这一时间爆火的“现代纪录片”中&#xff0c;麦克阿瑟总是对各种“名人”有不同的评价&…

springboot异步文件上传获取输入流提示找不到文件java.io.FileNotFoundException

springboot上传文件&#xff0c;使用异步操作处理上传的文件数据&#xff0c;出现异常如下&#xff1a; 这个是在异步之后使用传过来的MultipartFile对象尝试调用getInputStream方法发生的异常。 java.io.FileNotFoundException: C:\Users\Administrator\AppData\Local\Temp\to…

JAVA免杀学习与实验

1 认识Webshell 创建一个JSP文件&#xff1a; <% page import"java.io.InputStream" %> <% page import"java.io.BufferedReader" %> <% page import"java.io.InputStreamReader" %> <% page language"java" p…

【java安全】Log4j反序列化漏洞

文章目录 【java安全】Log4j反序列化漏洞关于Apache Log4j漏洞成因CVE-2017-5645漏洞版本复现环境漏洞复现漏洞分析 CVE-2019-17571漏洞版本漏洞复现漏洞分析 参考 【java安全】Log4j反序列化漏洞 关于Apache Log4j Log4j是Apache的开源项目&#xff0c;可以实现对System.out…

使用metasploit(MSF)对windows的ms17-010漏洞进行利用

主机发现 Ping扫描-禁用端口扫描 nmap -sn 192.168.1.0/24 ─# nmap -sn 192.168.1.0/24 Starting Nmap 7.93 ( https://nmap.org ) at 2023-08-14 10:00 EDT Nmap scan report for 192.168.1.1 Host is up (0.0053s latency). MAC Address: FC:BC:D1:C7:2F:A8 (Huawei Te…

OpenHarmony Meetup 广州站 OpenHarmony正当时—技术开源

招募令 OpenHarmony Meetup 广州站 火热招募中&#xff0c;等待激情四射的开发者&#xff0c;线下参与OpenHarmonyMeetup线下交流 展示前沿技术、探讨未来可能、让你了解更多专属OpenHarmony的魅力 线下参与&#xff0c;先到先得,仅限20个名额&#xff01; 报名截止时间8月23日…

[HZNUCTF 2023 preliminary] 2023杭师大校赛(初赛) web方向题解wp 全

ezflask 先看题目&#xff0c;应该是模板注入&#xff08;SSTI&#xff09;&#xff0c;输入{{7*‘7’}}直接报错误。 发现模板是反序输出的&#xff0c;怪不得不能直接输入{{}}。 输入}}‘7’*7{{返回777777&#xff0c;是jinja2 //直接手打&#xff0c;无所谓我是怨种 ?nam…
最新文章