動態

詳情 返回 返回

Golang 上傳文件到 MinIO?別瞎折騰了,這 5 個庫拿去用 - 動態 詳情

米娜桑,日常開發裏,有誰是沒有跟文件上傳下載打交道的?圖片、視頻、日誌、備份……這些往哪兒擱?一個很不錯的選擇就是對象存儲,而 MinIO 就是其中的明星選手。

MinIO 是一個用 Go 寫的、開源的、能自己部署的對象存儲服務,API 完全兼容亞馬遜 S3,可以算是自己私有的阿里雲 OSS 或者亞馬遜 S3,數據攥在自己手裏,美滋滋。

不過,在享受 MinIO 帶來的便利之前,得先把它跑起來。官方文檔要求輸入一大堆 docker run 命令,掛載一堆目錄,配置一堆環境變量……要是對 Docker 不熟,那可真有的折騰了。什麼端口衝突、數據持久化、網絡配置,分分鐘就是從入門到放棄。

這時候就可以考慮開發神器 ServBay 了,它除了支持各種常用開發語言 —— Python、Java、Golang,還有常用SQL和NoSQL數據庫,它還支持 MinIO 這種常用的服務。用户就不需要跟 Docker 命令死磕,也不用管什麼複雜的依賴配置,在 ServBay 面板上點幾下,一個熱乎乎的 MinIO 服務就跑起來了,省下來的時間摸摸魚、喝喝茶,爽歪歪。

用 ServBay 一鍵搞定了 MinIO 服務後,萬事俱備,只欠代碼。那用 Go 把文件傳到 MinIO,有哪些好用的庫?我給你挑了 5 個,各有千秋,任君選擇。

MinIO 官方 Go SDK (minio-go)

這可是親兒子,MinIO 官方自己維護的庫。兼容性、功能支持肯定是最好的,用起來也最符合直覺。如果是沒啥特殊癖好,就用它,準沒錯。

代碼示例

package main

import (
        "context"
        "log"

        "github.com/minio/minio-go/v7"
        "github.com/minio/minio-go/v7/pkg/credentials"
)

func main() {
        ctx := context.Background()
        // MinIO 配置
        minioHost := "127.0.0.1:9000"
        accessKeyID := "minioadmin" // 默認的 admin 賬號
        secretAccessKey := "minioadmin" // 默認的 admin 密碼
        useSSL := false // 本地開發環境,一般不用 SSL, 如果你是用ServBay,本地開發環境也是能用SSL的

        // 初始化 MinIO 客户端
        minioClient, err := minio.New(minioHost, &minio.Options{
                Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
                Secure: useSSL,
        })
        if err != nil {
                log.Fatalln("初始化 MinIO 客户端失敗:", err)
        }

        // 準備上傳
        bucketName := "learning-go"
        objectName := "my-first-object.txt"
        filePath := "./hello.txt" // 確保這個文件存在

        // 檢查 bucket 是否存在,不存在就創建一個
        exists, err := minioClient.BucketExists(ctx, bucketName)
        if err != nil {
                log.Fatalln("檢查存儲桶失敗:", err)
        }
        if !exists {
                err = minioClient.MakeBucket(ctx, bucketName, minio.MakeBucketOptions{})
                if err != nil {
                        log.Fatalln("創建存儲桶失敗:", err)
                }
                log.Printf("成功創建存儲桶: %s\n", bucketName)
        }

        // 上傳文件
        uploadInfo, err := minioClient.FPutObject(ctx, bucketName, objectName, filePath, minio.PutObjectOptions{})
        if err != nil {
                log.Fatalln("上傳文件失敗:", err)
        }

        log.Printf("文件上傳成功!對象名: %s, 大小: %d 字節\n", objectName, uploadInfo.Size)
}

代碼特點:邏輯清晰,FPutObject 直接從文件路徑上傳,非常方便。官方庫嘛,就是這麼貼心。

AWS SDK for Go (aws-sdk-go)

既然 MinIO 兼容 S3,那我用 AWS 的官方 SDK 操作它,合情合理吧?

那必須的!很多老項目可能已經集成了 AWS 的 SDK,遷移到 MinIO 或者混合使用時,用這個庫就完全不用改代碼,平滑過渡。

代碼示例:

package main

import (
        "log"
        "os"

        "github.com/aws/aws-sdk-go/aws"
        "github.com/aws/aws-sdk-go/aws/credentials"
        "github.com/aws/aws-sdk-go/aws/session"
        "github.com/aws/aws-sdk-go/service/s3/s3manager"
)

func main() {
        // MinIO 配置
        minioEndpoint := "http://127.0.0.1:9000" 
        accessKey := "minioadmin"
        secretKey := "minioadmin"
        region := "us-east-1" // MinIO 不關心 region,但 AWS SDK 需要,隨便填一個
        bucket := "go-aws-sdk-bucket"
        localFilePath := "./hello.txt" // 確保這個文件存在
        objectKey := "greeting.txt"

        // 創建 AWS session
        sess, err := session.NewSession(&aws.Config{
                Credentials:      credentials.NewStaticCredentials(accessKey, secretKey, ""),
                Endpoint:         aws.String(minioEndpoint),
                Region:           aws.String(region),
                S3ForcePathStyle: aws.Bool(true), // 這個對 MinIO 很重要,必須是 true
        })
        if err != nil {
                log.Fatalf("創建 AWS session 失敗: %v", err)
        }

        // 打開本地文件
        file, err := os.Open(localFilePath)
        if err != nil {
                log.Fatalf("打開文件失敗: %v", err)
        }
        defer file.Close()

        // 創建一個上傳器
        uploader := s3manager.NewUploader(sess)

        // 執行上傳
        _, err = uploader.Upload(&s3manager.UploadInput{
                Bucket: aws.String(bucket),
                Key:    aws.String(objectKey),
                Body:   file,
        })
        if err != nil {
                log.Fatalf("上傳文件到 MinIO 失敗: %v", err)
        }

        log.Printf("文件已通過 AWS SDK 成功上傳到: %s/%s\n", bucket, objectKey)
}

代碼特點:s3manager 提供了高級封裝,能自動處理大文件的分片上傳,很省心。記得 S3ForcePathStyle 一定要設成 true

Go Cloud (gocloud.dev)

如果想寫出“與平台無關”的代碼,今天用 MinIO,明天想換成 Google Cloud Storage,後天又想試試 Azure Blob Storage,而且不想改業務邏輯代碼,那 Go Cloud 就是最佳選擇,它提供了一套統一的 blob (Binary Large Object) 操作接口。

代碼示例:

package main

import (
        "context"
        "fmt"
        "log"
        "os"

        "gocloud.dev/blob"
        _ "gocloud.dev/blob/s3blob" // 別忘了匿名導入 S3 驅動
)

func main() {
        // MinIO 配置
        minioHost := "127.0.0.1:9000"
        accessKey := "minioadmin"
        secretKey := "minioadmin"
        bucketName := "gocloud-bucket"
        localFile := "./hello.txt"
        objectKey := "cloud-file.txt"

        // 用 URL 的方式配置連接,這是 Go Cloud 的特色
        // 注意參數:endpoint, disableSSL, s3ForcePathStyle
        bucketURL := fmt.Sprintf("s3://%s?endpoint=%s&access_key_id=%s&secret_access_key=%s&disableSSL=true&s3ForcePathStyle=true",
                bucketName, minioHost, accessKey, secretKey)

        ctx := context.Background()
        // 打開一個 bucket
        b, err := blob.OpenBucket(ctx, bucketURL)
        if err != nil {
                log.Fatalf("打開 bucket 失敗: %v", err)
        }
        defer b.Close()

        // 讀取本地文件內容
        data, err := os.ReadFile(localFile)
        if err != nil {
                log.Fatalf("讀取文件失敗: %v", err)
        }

        // 寫入對象存儲
        err = b.WriteAll(ctx, objectKey, data, nil)
        if err != nil {
                log.Fatalf("上傳文件失敗: %v", err)
        }

        log.Printf("文件已通過 Go Cloud 成功上傳到: %s/%s\n", bucketName, objectKey)
}

代碼特點:核心代碼就 blob.OpenBucketb.WriteAll 兩步,非常抽象。那個長長的 bucketURL 就是配置的關鍵,把所有參數都塞進去了。

go-storage

go-storage 是另一個存儲抽象庫,理念和 Go Cloud 類似,但出自不同的社區。它的目標也是提供一套統一的 API 來操作各種存儲服務。如果你覺得 Go Cloud 不合胃口,可以試試這個。

代碼示例:

package main

import (
        "context"
        "fmt"
        "log"
        "os"

        "github.com/beyondstorage/go-storage/v5/services"
        "github.com/beyondstorage/go-storage/v5/types"
)

func main() {
        // MinIO 配置
        minioHost := "127.0.0.1:9000"
        accessKey := "minioadmin"
        secretKey := "minioadmin"
        bucketName := "go-storage-bucket"
        filePath := "./hello.txt"
        objectName := "storage-api-file.txt"

        // 同樣是連接字符串的玩法
        connStr := fmt.Sprintf("s3://%s@%s?credential=hmac:%s:%s&endpoint=http://%s",
                bucketName, "us-east-1", accessKey, secretKey, minioHost)

        // 從字符串創建存儲服務實例
        store, err := services.NewStoragerFromString(connStr)
        if err != nil {
                log.Fatalf("創建 storager 失敗: %v", err)
        }

        // 打開文件
        file, err := os.Open(filePath)
        if err != nil {
                log.Fatalf("打開文件失敗: %v", err)
        }
        defer file.Close()

        // 獲取文件信息,主要是為了拿到大小
        stat, _ := file.Stat()
        fileSize := stat.Size()

        // 執行寫入操作
        _, err = store.Write(context.Background(), objectName, file, fileSize, types.WithForcePair(true))
        if err != nil {
                log.Fatalf("寫入對象失敗: %v", err)
        }

        log.Printf("文件已通過 go-storage 成功上傳到: %s/%s\n", bucketName, objectName)
}

代碼特點:API 風格和 Go Cloud 不太一樣,連接字符串的格式也不同。store.Write 需要傳入文件大小,這點要注意。

直接使用 AWS SDK for Go 的底層 S3 客户端

前面我們用了 s3manager,那是高級封裝。但有時候需要更精細的控制,比如設置特定的元數據、ACL 等,這時候就可以直接用底層的 s3 客户端。

代碼示例:

package main

import (
        "log"
        "os"

        "github.com/aws/aws-sdk-go/aws"
        "github.com/aws/aws-sdk-go/aws/credentials"
        "github.comcom/aws/aws-sdk-go/aws/session"
        "github.com/aws/aws-sdk-go/service/s3"
)

func main() {
        // MinIO 配置
        minioEndpoint := "http://127.0.0.1:9000"
        accessKey := "minioadmin"
        secretKey := "minioadmin"
        region := "us-east-1"
        bucket := "low-level-bucket"
        localFilePath := "./hello.txt"
        objectKey := "detail-control.txt"

        // 配置和之前一樣
        awsConfig := &aws.Config{
                Credentials:      credentials.NewStaticCredentials(accessKey, secretKey, ""),
                Endpoint:         aws.String(minioEndpoint),
                Region:           aws.String(region),
                S3ForcePathStyle: aws.Bool(true),
        }

        sess := session.Must(session.NewSession(awsConfig))

        // 直接創建 S3 服務客户端,而不是 s3manager
        s3Client := s3.New(sess)

        file, err := os.Open(localFilePath)
        if err != nil {
                log.Fatalf("打開文件失敗: %v", err)
        }
        defer file.Close()

        // 直接調用 PutObject API
        _, err = s3Client.PutObject(&s3.PutObjectInput{
                Bucket: aws.String(bucket),
                Key:    aws.String(objectKey),
                Body:   file,
                // 在這裏可以設置更多參數,比如 ACL, ContentType 等
                // ACL:    aws.String("public-read"),
        })

        if err != nil {
                log.Fatalf("PutObject 失敗: %v", err)
        }

        log.Printf("文件已通過底層 S3 客户端成功上傳到: %s/%s\n", bucket, objectKey)
}

代碼特點:這個方法給了你最大的靈活性,PutObjectInput 結構體裏有很多字段可以配置,適合需要深度定製上傳行為的場景。

總結一下

優點 適合場景
minio-go 官方支持,功能最全,API 直觀 新項目,或只和 MinIO 打交道的項目
aws-sdk-go (s3manager) 社區龐大,自動處理大文件,S3 生態通用 已在使用 AWS SDK 的項目,需要處理大文件上傳
gocloud.dev 高度抽象,平台無關,代碼可移植性強 需要兼容多種對象存儲,不想被廠商綁定的架構
go-storage 另一個優秀的抽象庫,API 設計有特色 Go Cloud 的替代品,看個人編碼喜好
aws-sdk-go (s3 client) 控制粒度最細,靈活性最高 需要精細控制上傳參數(元數據、權限等)的場景

選擇哪個庫,沒有絕對的好壞,看項目需求和個人偏好。

但無論是選擇哪個庫,一個穩定、易於管理的 MinIO 環境都是你施展拳腳的前提。再次安利一下 ServBay,它能把搭建環境的麻煩事變得更加輕鬆便利,代碼都比同事寫得快很多。

user avatar u_17037082 頭像 qingzhan 頭像 AmbitionGarden 頭像 wmbuke 頭像 youyoufei 頭像 it1042290135 頭像 boxuegu 頭像 zego 頭像 daguaisou 頭像 king2088 頭像 xiaoxiansheng_5e75673e1ae30 頭像 wxweven 頭像
點贊 22 用戶, 點贊了這篇動態!
點贊

Add a new 評論

Some HTML is okay.