go: MinIO基本用法 | 青训营

483 阅读4分钟

尽管本人的项目比较玩具, 但还是采用了OSS来存储视频等大文件. 本文将介绍MinIO的go客户端的基本用法.

其实开发伊始, 选取MinIO作为对象存储是考虑到了它既开源又完善兼容Amazon S3, 泛用性很强. 它的搭建也很简单, 只需要podman pull docker.io/minio/minio, 并在启动前利用-p暴露端口, 利用-v做持久化, 利用-e通过设置环境变量设置账号密码等即可. 但MinIO很纯粹, 没有那些特色的云端处理音视频等的功能, 使玩具项目的封面切取遭到了一定的困难. 本节末将提出一种解决方案. 首先先介绍MinIO的go客户端的上传下载删除等.

  1. 所有的开始, 新建MinIO客户端.

    package main
    
    import (
            "log"
    
            "github.com/minio/minio-go/v7"
            "github.com/minio/minio-go/v7/pkg/credentials"
    )
    
    func main() {
            endpoint := "play.min.io"
            accessKeyID := "Q3AM3UQ867SPQQA43P2F"
            secretAccessKey := "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG"
            useSSL := true
    
            // Initialize minio client object.
            minioClient, err := minio.New(endpoint, &minio.Options{
                    Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
                    Secure: useSSL,
            })
            if err != nil {
                    log.Fatalln(err)
            }
    
            log.Printf("%#v\n", minioClient) // minioClient is now setup
    }
    
  2. 上传其实还蛮简单的. 最常用的应有两种方法.

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

    需要指定字节流, 文件大小和老生常谈的存储桶名和目标对象名即可完成上传. 可用context控制, 这点上MinIO几乎所有API都支持, 很系统很整齐, 很好评. 需要注意的是, 文件大小可以设为-1来自动检测, 但是这样将会占用大量内存空间! 个人还是很不建议的.

    • 第二种:
    FPutObject(ctx context.Context, bucketName, objectName, filePath, opts PutObjectOptions) (info UploadInfo, err error)
    

    和第一种类似, 但指定路径即可上传文件. 不过个人不建议这样做. 因为在项目中, 文件来源往往是用户的上传, 本身为字节流(multipart). 若使用文件上传方式, 需要先将其下载到服务器上, 多次读写硬盘与内存, 性能十分低下, 且占用硬盘空间. 不如采用第一种.

    两种上传方式都会默认覆盖云端文件! 使用时需注意.

  3. 删除其实更简单, 因为只在云端操作.

    • 单个对象的删除
    RemoveObject(ctx context.Context, bucketName, objectName string, opts minio.RemoveObjectOptions) error
    
    • 多个对象的删除
    RemoveObjects(ctx context.Context, bucketName string, objectsCh <-chan ObjectInfo, opts RemoveObjectsOptions) <-chan RemoveObjectError
    

    批量删除一次最大1000个对象. 但要注意批量删除时错误是通过channel返回的, 而非单个.

  4. 上传和删除都解决了, 那么怎么获取文件对象呢? MinIO提供了简便的下载API, 这一点完胜七牛云等.

    • 老规矩, 首先是流式:
    GetObject(ctx context.Context, bucketName, objectName string, opts GetObjectOptions) (*Object, error)
    
    • 然后是文件式:
    FGetObject(ctx context.Context, bucketName, objectName, filePath string, opts GetObjectOptions) error
    

    既然是下载, 多数时候会下载到硬盘上, 就没上传那样的性能顾虑了. 个人推荐第二种(实际上就是第一种封装过了), 不用手动close文件, 越省事越好.

  5. 最后就是OSS功能里最喜闻乐见的签发URL了

    • 首先是签发获取对象用的预签名URL:
    PresignedGetObject(ctx context.Context, bucketName, objectName string, expiry time.Duration, reqParams url.Values) (*url.URL, error)
    

    注意签发URL是在本地进行的! 不需要联网也不需要对象真正存在! (可能这就是为啥叫预签名URL) 并可以设置有效期, 缓存这种URL时要注意! 同时还有一个大坑, 签发URL所用的对象存储服务地址就是初始化客户端时的地址! 并不会做任何改变! 且可以与MINIO_SERVER_URL变量不同! 小心别用内网地址了.

    • 然后是签发用于上传对象的预签名URL
    PresignedPutObject(ctx context.Context, bucketName, objectName string, expiry time.Duration) (*url.URL, error)
    

    向前端分发这个URL即可实现不经过server直接上传到OSS, 节约server带宽, 减少server性能消耗. 可惜我的玩具项目的前端不支持, 实在是性能之大敌. 如此无语.

  6. 最后提一下如何切取封面. 本人目前的方案是, 先用字节流上传视频, 上传视频的同时上传一个默认封面, 上传成功后直接返回响应. 然后开启goroutine, 异步下载视频回切取服务器, 切取真实封面后上传替换掉默认封面. 可以实现响应速度与封面切取间的平衡.