Skip to content

Latest commit

 

History

History
279 lines (205 loc) · 5.86 KB

File metadata and controls

279 lines (205 loc) · 5.86 KB

3.10 对象存储(MinIO)

📍 导航返回目录 | 上一节:RabbitMQ


MinIO 介绍

高性能、S3兼容的对象存储系统

特点

  • 兼容AWS S3 API
  • 支持分布式部署
  • 开源免费
  • Go语言编写

Docker部署

docker run -p 9000:9000 -p 9001:9001 \
  -e "MINIO_ROOT_USER=minioadmin" \
  -e "MINIO_ROOT_PASSWORD=minioadmin" \
  minio/minio server /data --console-address ":9001"

访问:http://localhost:9001


Go SDK使用

初始化客户端

package main

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

func NewMinIOClient() (*minio.Client, error) {
    return minio.New("localhost:9000", &minio.Options{
        Creds:  credentials.NewStaticV4("minioadmin", "minioadmin", ""),
        Secure: false,
    })
}

创建Bucket

func CreateBucket(client *minio.Client, bucketName string) error {
    ctx := context.Background()
    
    exists, err := client.BucketExists(ctx, bucketName)
    if err != nil {
        return err
    }
    
    if !exists {
        err = client.MakeBucket(ctx, bucketName, minio.MakeBucketOptions{})
        if err != nil {
            return err
        }
        log.Printf("Bucket %s created", bucketName)
    }
    
    return nil
}

上传文件

func UploadFile(client *minio.Client, bucketName, objectName, filePath string) error {
    ctx := context.Background()
    
    info, err := client.FPutObject(ctx, bucketName, objectName, filePath, minio.PutObjectOptions{
        ContentType: "application/octet-stream",
    })
    
    if err != nil {
        return err
    }
    
    log.Printf("Uploaded %s of size %d", objectName, info.Size)
    return nil
}

下载文件

func DownloadFile(client *minio.Client, bucketName, objectName, savePath string) error {
    ctx := context.Background()
    
    return client.FGetObject(ctx, bucketName, objectName, savePath, minio.GetObjectOptions{})
}

生成预签名URL

func GeneratePresignedURL(client *minio.Client, bucketName, objectName string, expiry time.Duration) (string, error) {
    ctx := context.Background()
    
    // 生成下载URL(有效期7天)
    presignedURL, err := client.PresignedGetObject(ctx, bucketName, objectName, expiry, nil)
    if err != nil {
        return "", err
    }
    
    return presignedURL.String(), nil
}

大文件上传(分片)

func UploadLargeFile(client *minio.Client, bucketName, objectName, filePath string) error {
    ctx := context.Background()
    
    file, err := os.Open(filePath)
    if err != nil {
        return err
    }
    defer file.Close()
    
    fileStat, _ := file.Stat()
    
    // 分片上传(自动处理)
    _, err = client.PutObject(ctx, bucketName, objectName, file, fileStat.Size(), minio.PutObjectOptions{
        ContentType: "application/octet-stream",
        PartSize:    10 * 1024 * 1024, // 10MB分片
    })
    
    return err
}

生产环境配置

分布式部署

# 4节点集群
export MINIO_ROOT_USER=admin
export MINIO_ROOT_PASSWORD=password

# 节点1
minio server http://node{1...4}/data{1...4}

# 自动实现:
# - 数据分片(Erasure Coding)
# - 高可用(N/2节点故障仍可用)
# - 负载均衡

对象生命周期

// 设置生命周期规则(30天后删除)
config := lifecycle.NewConfiguration()
config.Rules = []lifecycle.Rule{
    {
        ID:     "expire-old-logs",
        Status: "Enabled",
        Expiration: lifecycle.Expiration{
            Days: 30,
        },
        Filter: lifecycle.Filter{
            Prefix: "logs/",
        },
    },
}

client.SetBucketLifecycle(ctx, bucketName, config)

实战:图片服务

type ImageService struct {
    minio *minio.Client
}

// 上传图片
func (is *ImageService) UploadImage(imageFile multipart.File, filename string) (string, error) {
    ctx := context.Background()
    
    // 生成唯一文件名
    objectName := fmt.Sprintf("images/%s-%s", uuid.New().String(), filename)
    
    // 上传
    _, err := is.minio.PutObject(ctx, "my-bucket", objectName, imageFile, -1, minio.PutObjectOptions{
        ContentType: "image/jpeg",
    })
    
    if err != nil {
        return "", err
    }
    
    // 生成访问URL
    url, _ := is.GenerateURL(objectName, 7*24*time.Hour)
    
    return url, nil
}

// HTTP Handler
func (is *ImageService) HandleUpload(w http.ResponseWriter, r *http.Request) {
    file, header, err := r.FormFile("image")
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    defer file.Close()
    
    url, err := is.UploadImage(file, header.Filename)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    
    json.NewEncoder(w).Encode(map[string]string{
        "url": url,
    })
}

MinIO vs OSS/S3

特性 MinIO 阿里云OSS AWS S3
部署 自建 云服务 云服务
成本 低(硬件成本) 按量计费 按量计费
可控性
运维 需要 无需 无需
API S3兼容 兼容S3 原生

本章小结

关键要点

  • ✅ MinIO提供S3兼容的对象存储
  • ✅ 适合存储图片、视频、文件
  • ✅ 支持分布式部署
  • ✅ 预签名URL实现安全访问

扩展阅读


💡 思考题

  1. 对象存储和文件存储的区别?
  2. 如何实现断点续传?
  3. 如何保证上传的安全性?

⏮️ 上一节:RabbitMQ | ⏏️ 返回目录