MinIO使用笔记

最近,我刚刚完成了一个异步任务,所以也顺手整理了下 MinIO 对象存储的使用笔记,防止之后忘记。

MinIO 是一个非常流行的开源对象存储服务,兼容 Amazon S3 协议,特别适合存储图片、视频、文档等非结构化数据,可以用来搭配 RabbitMq (消息队列)做一些异步任务。


一、 准备工作

1.1 安装依赖

首先,安装 MinIO 的 Python SDK:

复制代码
pip install minio

1.2 编写配置文件

在开始编码之前,我们需要配置一下参数。创建一个 config.json 文件:

复制代码
{
    "miniIOConfig": {
        "bucketName": "your-bucket",
        "endpoint": "https://your-minio-server.com",
        "accessKey": "admin",
        "secretKey": "your-secret-key"
    }
}
配置项 说明
bucketName 存储桶名称(类似于文件夹)
endpoint MiniIO 服务地址
accessKey 访问密钥(用户名)
secretKey 秘密密钥(密码)

二、 核心类实现

为了方便调用,我们将所有的连接和操作逻辑封装成一个 MiniIOClient 类。新建 miniIOUse.py,填入以下代码:

复制代码
import json
import os
import io
from datetime import timedelta
from minio import Minio
from minio.error import S3Error

class MiniIOClient:
    def __init__(self, config_path="config.json"):
        # 读取配置文件
        with open(config_path, 'r', encoding='utf-8') as f:
            config = json.load(f).get("miniIOConfig")
        
        self.bucket_name = config.get("bucketName")
        endpoint = config.get("endpoint")
        access_key = config.get("accessKey")
        secret_key = config.get("secretKey")
        
        # 判断是否为 HTTPS
        secure = endpoint.startswith("https")
        # 去除协议头,Minio 类只需要 host:port
        if endpoint.startswith("https://") or endpoint.startswith("http://"):
            endpoint = endpoint.split("://")[1]
            
        self.client = Minio(
            endpoint,
            access_key=access_key,
            secret_key=secret_key,
            secure=secure
        )

    def ensure_bucket_exists(self):
        """检查存储桶是否存在,不存在则创建"""
        ifnot self.client.bucket_exists(self.bucket_name):
            self.client.make_bucket(self.bucket_name)

    def upload_file(self, file_path, object_name=None, content_type=None):
        """上传本地文件"""
        if object_name isNone:
            object_name = os.path.basename(file_path)
        self.client.fput_object(
            self.bucket_name, 
            object_name, 
            file_path, 
            content_type=content_type
        )

    def upload_bytes(self, data, object_name, length=None, content_type="application/octet-stream"):
        """直接上传字节数据或文本"""
        if isinstance(data, str):
            data = data.encode('utf-8')
        
        if length isNone:
            length = len(data)
            
        data_stream = io.BytesIO(data)
        self.client.put_object(
            self.bucket_name, 
            object_name, 
            data_stream, 
            length, 
            content_type=content_type
        )

    def download_file(self, object_name, file_path=None):
        """下载文件到本地或读取到内存"""
        try:
            response = self.client.get_object(self.bucket_name, object_name)
            data = response.read()
            response.close()
            response.release_conn()
            
            if file_path:
                with open(file_path, 'wb') as f:
                    f.write(data)
                returnTrue
            return data
        except S3Error as e:
            if e.code == 'NoSuchKey':
                returnNone
            raise

    def delete_file(self, object_name):
        """删除文件"""
        try:
            self.client.remove_object(self.bucket_name, object_name)
            returnTrue
        except S3Error:
            returnFalse

    def list_files(self, prefix=None, recursive=False):
        """列出文件"""
        objects = self.client.list_objects(
            self.bucket_name, 
            prefix=prefix, 
            recursive=recursive
        )
        result = []
        for obj in objects:
            if obj.is_dir:
                continue
            result.append({
                'name': obj.object_name,
                'size': obj.size,
                'last_modified': obj.last_modified,
                'etag': obj.etag
            })
        return result

    def file_exists(self, object_name):
        """检查文件是否存在"""
        try:
            self.client.stat_object(self.bucket_name, object_name)
            returnTrue
        except S3Error:
            returnFalse

    def get_file_info(self, object_name):
        """获取文件元数据"""
        try:
            stat = self.client.stat_object(self.bucket_name, object_name)
            return {
                'name': stat.object_name,
                'size': stat.size,
                'last_modified': stat.last_modified,
                'etag': stat.etag,
                'content_type': stat.content_type
            }
        except S3Error:
            returnNone

    def get_presigned_url(self, object_name, expires=3600):
        """生成预签名分享链接"""
        return self.client.presigned_get_object(
            self.bucket_name, 
            object_name, 
            expires=timedelta(seconds=expires)
        )

三、 操作指南

3.1 初始化客户端与检查存储桶

复制代码
from miniIOUse import MiniIOClient

# 使用默认配置文件
client = MiniIOClient()

# 或指定配置文件路径
# client = MiniIOClient("custom_config.json")

# 自动检查存储桶是否存在,不存在则创建
client.ensure_bucket_exists()

3.2 上传本地文件

复制代码
# 基础用法:使用原文件名
client.upload_file("/your/file_path/file.pdf")

# 高级用法:指定对象名称和MIME类型
client.upload_file(
    "example.pdf", 
    "your-bucket/folder/example.pdf", 
    "application/pdf"
)

常用 MIME 类型

文件类型 MIME 类型
纯文本 text/plain
PDF文档 application/pdf
JPEG图片 image/jpeg
PNG图片 image/png
JSON数据 application/json
二进制数据 application/octet-stream

3.3 直接上传字节数据

无需本地文件,直接上传内存中的数据:

复制代码
# 上传文本
client.upload_bytes(
    "Hello World", 
    "greeting.txt", 
    11, 
    "text/plain"
)

# 上传二进制数据
client.upload_bytes(
    b'\x00\x01\x02', 
    "binary.bin", 
    3, 
    "application/octet-stream"
)

参数说明

  • data:要上传的数据(字符串或字节)

  • object_name:存储的对象名称

  • length:数据长度(字节数,若不填会自动计算)

  • content_type:MIME类型(可选)


3.4 下载文件

复制代码
# 仅读取内容到内存
content = client.download_file("example.pdf")
if content:
    print(f"下载了 {len(content)} 字节")

# 下载并保存到本地文件
client.download_file("example.pdf", "/your/path/save.pdf")

返回值 :仅读取时返回文件内容的字节数据,失败返回 None;保存到本地时成功返回 True


3.5 删除文件

复制代码
client.delete_file("old_example.pdf")

返回值 :成功返回 True,失败返回 False


3.6 列出文件

复制代码
# 列出所有文件
all_files = client.list_files()

# 只列出特定目录下的文件
docs = client.list_files(prefix="example/")

# 递归列出所有文件(包括子目录)
all_recursive = client.list_files(recursive=True)

# 遍历结果
for file in all_files:
    print(f"文件名: {file['name']}")
    print(f"大小: {file['size']} bytes")
    print(f"修改时间: {file['last_modified']}")
    print(f"ETag: {file['etag']}")
    print("-" * 30)

3.7 检查文件是否存在

复制代码
if client.file_exists("exanple.pdf"):
    print("✅ 文件存在")
else:
    print("❌ 文件不存在")

3.8 获取文件元数据

复制代码
info = client.get_file_info("example.pdf")

if info:
    print(f"文件名: {info['name']}")
    print(f"文件大小: {info['size']} bytes")
    print(f"最后修改: {info['last_modified']}")
    print(f"ETag: {info['etag']}")
    print(f"文件类型: {info['content_type']}")

3.9 生成分享链接(预签名URL)

复制代码
# 生成1小时有效的分享链接
share_url = client.get_presigned_url("example.pdf")
print(f"分享链接(1小时有效): {share_url}")

# 生成24小时有效的链接
long_url = client.get_presigned_url(
    "example.pdf", 
    expires=86400  # 24小时 = 86400秒
)
print(f"分享链接(24小时有效): {long_url}")

四、 完整示例

把上面的功能串联起来,就是一个完整的示例:

复制代码
from miniIOUse import MiniIOClient

def main():
    # 1. 正在连接 MiniIO
    client = MiniIOClient("config.json")
    
    # 2. 检查存储桶
    client.ensure_bucket_exists()
    
    # 3. 上传文件
    client.upload_file(
        "example.pdf", 
        "your/file_path/example.pdf", 
        "application/pdf"
    )
    
    # 4. 上传文本数据
    client.upload_bytes(
        "重要通知内容", 
        "notices/example.txt", 
        19, 
        "text/plain"
    )
    
    # 5. 检查文件是否存在
    if client.file_exists("your/path/example.pdf"):
        print("✅ 文件上传成功")
    
    # 6. 获取文件信息
    info = client.get_file_info("your/path/example.pdf")
    print(f"文件大小: {info['size']} bytes")
    
    # 7. 列出文件
    client.list_files(prefix="example/", recursive=True)
    
    # 8. 下载文件
    client.download_file(
        "your/path/example.pdf", 
        "downloaded.pdf"
    )
    
    # 9. 生成分享链接
    share_url = client.get_presigned_url(
        "your/path/example.pdf", 
        expires=7200
    )
   
    # 10. 清理测试文件
    client.delete_file("example/example.txt")

if __name__ == "__main__":
    main()

五、 注意事项

5.1 HTTPS vs HTTP

代码已实现自动判断,依据 config.jsonendpoint 是否以 https:// 开头来决定。若是 HTTP 协议,配置时写成 http://your-minio-server.com 即可。

5.2 路径分隔符

对象名称中的路径建议统一使用 / 作为分隔符:

复制代码
"your/path/example.pdf"

5.3 中文文件名

确保使用 UTF-8 编码,避免乱码问题。

5.4 大文件上传

对于大于 100MB 的文件,建议使用分片上传(fput_object 方法会自动处理)。

5.5 权限控制

确保配置文件中的 accessKeysecretKey 有足够的权限操作目标存储桶。

5.6 异常处理

所有方法都可能抛出 S3Error 异常,建议在实际生产环境中添加异常处理:

复制代码
from minio.error import S3Error

try:
    client.upload_file("file.pdf")
except S3Error as e:
    print(f"操作失败: {e}")
相关推荐
東雪木5 小时前
多线程与并发编程 专属复习笔记
java·开发语言·笔记·java面试
Oll Correct5 小时前
实验二十九:TCP的运输连接管理
网络·笔记
飞翔中文网7 小时前
Java学习笔记之抽象类与接口(设计思想)
java·笔记·学习
智者知已应修善业7 小时前
【proteus设计文氏正弦波信号发生器】2023-5-9
驱动开发·经验分享·笔记·硬件架构·proteus·硬件工程
凉、介9 小时前
深入理解 ARMv8-A|处理器模式与寄存器
笔记·学习·嵌入式·arm
whyTeaFo9 小时前
MIT 6.1810: Lec 5: calling conventions and stack frames RISC-V
笔记
上课不要睡觉了10 小时前
【统计法规】4.1统计管理体制概述
笔记·统计师考试
墨白曦煜11 小时前
算法实战笔记:剥开回溯算法的外衣——从通用模板到高阶去重(八)
笔记·算法
Upsy-Daisy11 小时前
IOTA 学习笔记(四):当前 IOTA 架构总览
笔记·学习·架构
山楂树の12 小时前
JS中??和||的区别
笔记