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}")
相关推荐
金色光环1 小时前
【DSP学习笔记】 F28335中断系统理解-基于普中DSP28335开发攻略
笔记·单片机·学习·dsp开发
半导体守望者1 小时前
MKS MWD-25LD-06/07 匹配器Automatic Matching Network OPERATION MANUAL
经验分享·笔记·机器人·自动化·制造
晓梦林1 小时前
Laoda靶场学习笔记
笔记·学习
YYYing.1 小时前
【C++项目之高并发内存池 (四)】三层缓存的空间回收流程详解
c++·笔记·缓存·高并发·内存池
渣渣灰95872 小时前
嵌入式设备通信方式总结
笔记
IT英语写作研习社2 小时前
英语写作中“复杂的”complex complicated 的用法
笔记
handler012 小时前
速通蓝桥杯省一: 前缀和&差分(附经典例题)
c语言·c++·笔记·职场和发展·蓝桥杯
问心无愧051314 小时前
ctf show web入门37
笔记
羊群智妍15 小时前
2026生成式AI搜索优化:GEO监测工具全解析
笔记