在 Ubuntu 上安装 MinIO 并使用 Python 封装类操作对象存储

MinIO 是一个高性能、兼容 Amazon S3 API 的对象存储服务,非常适合小型私有云或者开发测试环境。本文将详细介绍在 Ubuntu 上安装 MinIO,并用 Python 封装类实现文件上传、下载、删除和批量操作。


一、MinIO 安装与配置

1. 下载 MinIO 二进制

复制代码
wget https://dl.min.io/server/minio/release/linux-amd64/minio -O /usr/local/bin/minio
sudo chmod +x /usr/local/bin/minio

确保 MinIO 可执行文件有执行权限。

2. 创建数据目录

复制代码
sudo mkdir -p /data/minio 
sudo chown -R $USER:$USER /data/minio

3. 以系统服务方式启动 MinIO(可选)

创建 systemd 服务文件 /etc/systemd/system/minio.service

复制代码
[Unit]
Description=MinIO Object Storage
After=network.target

[Service]
User=root
ExecStart=/usr/local/bin/minio server /data/minio --console-address ":9001"
Restart=always
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

启用并启动:

复制代码
sudo systemctl daemon-reload
sudo systemctl enable minio
sudo systemctl start minio
sudo systemctl status minio

注意:如果在 WSL 环境下,systemd 默认未启用,可直接用命令启动:

minio server /data/minio --console-address ":9001"

4. 测试 MinIO

访问浏览器:

  • Web 控制台 : http://127.0.0.1:9001

  • API 端口 : http://127.0.0.1:9000

默认用户名和密码可以在启动命令中通过环境变量指定:

复制代码
export MINIO_ROOT_USER=admin
export MINIO_ROOT_PASSWORD=admin123456

二、Python 操作 MinIO 的封装类

为了方便操作 MinIO 对象存储,我们可以写一个 Python 封装类 MinioClient,支持:

  • 文件上传/下载

  • 字节数据上传/下载

  • 删除文件

  • 列举对象

  • 批量上传/下载文件夹

  • 生成临时 URL

1. 安装 Python SDK

复制代码
pip install minio

2. 封装类代码

python 复制代码
from minio import Minio
from minio.error import S3Error
from io import BytesIO
import os

class MinioClient:
    def __init__(self, endpoint, access_key, secret_key, bucket_name, secure=False):
        """
        初始化 MinIO 客户端,并固定 bucket
        """
        self.client = Minio(endpoint, access_key=access_key, secret_key=secret_key, secure=secure)
        self.bucket_name = bucket_name
        # 自动创建 bucket
        if not self.client.bucket_exists(bucket_name):
            self.client.make_bucket(bucket_name)

    def upload_file(self, object_name, file_path, content_type=None):
        try:
            self.client.fput_object(self.bucket_name, object_name, file_path, content_type=content_type)
        except S3Error as e:
            raise RuntimeError(f"上传文件 {file_path} 到 {self.bucket_name}/{object_name} 失败: {e}")

    def upload_bytes(self, object_name, data: bytes, content_type="application/octet-stream"):
        try:
            self.client.put_object(self.bucket_name, object_name, BytesIO(data), length=len(data), content_type=content_type)
        except S3Error as e:
            raise RuntimeError(f"上传字节数据到 {self.bucket_name}/{object_name} 失败: {e}")

    def download_file(self, object_name, file_path):
        try:
            self.client.fget_object(self.bucket_name, object_name, file_path)
        except S3Error as e:
            raise RuntimeError(f"下载 {self.bucket_name}/{object_name} 到 {file_path} 失败: {e}")

    def download_bytes(self, object_name) -> bytes:
        try:
            response = self.client.get_object(self.bucket_name, object_name)
            data = response.read()
            response.close()
            response.release_conn()
            return data
        except S3Error as e:
            raise RuntimeError(f"下载 {self.bucket_name}/{object_name} 失败: {e}")

    def delete_object(self, object_name):
        try:
            self.client.remove_object(self.bucket_name, object_name)
        except S3Error as e:
            raise RuntimeError(f"删除 {self.bucket_name}/{object_name} 失败: {e}")

    def list_objects(self, prefix="", recursive=True):
        try:
            return [obj.object_name for obj in self.client.list_objects(self.bucket_name, prefix=prefix, recursive=recursive)]
        except S3Error as e:
            raise RuntimeError(f"列举 {self.bucket_name} 下对象失败: {e}")

    def presigned_get_url(self, object_name, expires=3600):
        try:
            return self.client.presigned_get_object(self.bucket_name, object_name, expires=expires)
        except S3Error as e:
            raise RuntimeError(f"生成下载链接失败: {e}")

    def presigned_put_url(self, object_name, expires=3600):
        try:
            return self.client.presigned_put_object(self.bucket_name, object_name, expires=expires)
        except S3Error as e:
            raise RuntimeError(f"生成上传链接失败: {e}")

    # ================= 批量操作文件夹 =================
    def upload_folder(self, local_folder, remote_folder=""):
        """上传本地目录到 MinIO"""
        if not os.path.isdir(local_folder):
            raise ValueError(f"{local_folder} 不是目录")
        for root, _, files in os.walk(local_folder):
            for file in files:
                local_path = os.path.join(root, file)
                relative_path = os.path.relpath(local_path, local_folder)
                object_name = os.path.join(remote_folder, relative_path).replace("\\", "/")
                self.upload_file(object_name, local_path)

    def download_folder(self, remote_folder, local_folder):
        """下载 MinIO 前缀到本地目录"""
        objects = self.list_objects(prefix=remote_folder, recursive=True)
        for obj_name in objects:
            relative_path = os.path.relpath(obj_name, remote_folder).replace("\\", "/")
            local_path = os.path.join(local_folder, relative_path)
            os.makedirs(os.path.dirname(local_path), exist_ok=True)
            self.download_file(obj_name, local_path)

3. 使用示例

python 复制代码
client = MinioClient("127.0.0.1:9000", "admin", "admin123456", bucket_name="mybucket")

# 上传单文件
client.upload_file("test.txt", "/tmp/test.txt")

# 上传字节数据
client.upload_bytes("hello.txt", b"Hello MinIO!")

# 下载文件
client.download_file("test.txt", "/tmp/test_download.txt")

# 下载到内存
data = client.download_bytes("hello.txt")
print(data.decode())

# 删除文件
client.delete_object("hello.txt")

# 列举对象
print(client.list_objects())

# 批量上传目录
client.upload_folder("/tmp/local_folder", "remote_folder")

# 批量下载目录
client.download_folder("remote_folder", "/tmp/download_folder")

# 临时下载 URL
url = client.presigned_get_url("test.txt")
print("Download URL:", url)

三、总结

  1. MinIO 是轻量级、兼容 S3 的对象存储,安装简单,适合私有云和开发环境。

  2. Python 官方 SDK minio 可以方便地操作对象存储,结合封装类 MinioClient 可以像操作本地文件一样管理文件和目录。

  3. 封装类支持单文件和批量文件夹操作,同时支持生成临时 URL,适合各种开发场景。

相关推荐
杰克崔39 分钟前
进程内mmap锁相互干扰问题
linux·运维·服务器·车载系统
木泽八39 分钟前
python实现pdf拆分与合并
服务器·python·pdf
恒创科技HK1 小时前
2026年香港服务器有哪些区域可选?
运维·服务器
xjxijd1 小时前
工业元宇宙 IDC 支撑:数字孪生算法 + 边缘服务器,生产调度响应速度提 3 倍
运维·服务器·算法
dblens 数据库管理和开发工具1 小时前
DBLens:让 SQL 查询更智能、更高效的数据库利器
服务器·数据库·sql·数据库连接工具·dblens
代码游侠2 小时前
应用——Linux进程通信与信号处理
linux·运维·服务器·笔记·学习·信号处理
HalvmånEver2 小时前
Linux:Ext系列⽂件系统(二)
linux·运维·服务器
scan7242 小时前
python mcp 打印出参数
linux·服务器·数据库
梓仁沐白2 小时前
操作系统:进程通信和死锁
linux·服务器·网络
Bruce_Liuxiaowei2 小时前
Nmap主机发现与在线主机提取实用指南
服务器·网络·安全