目录
本文介绍
MinIO 是一个开源的对象存储服务,它提供了简单而强大的 API,用于管理和操作存储桶中的对象。本文介绍了如何对 MinIO 的 API 进行封装,将其封装为易于使用的函数,并将封装后的函数上传到第三方库 Pyzjr 中。
上一节补充
上一章需要补充的一点是需要Python的版本要做 3.7 及以上的版本。
使用官方的游乐场进行测试和开发
官方提供了一个MinIO服务器游乐场 https://play.min.io,可以随意使用此服务进行测试和开发。
好,现在我们先要用这个游乐场进行上传文件,熟悉操作。
from minio import Minio
from minio.error import S3Error
def main():
# Create a client with the MinIO server playground, its access key
# and secret key.
client = Minio(
"play.min.io",
access_key="Q3AM3UQ867SPQQA43P2F",
secret_key="zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG",
)
# Make 'ikun' bucket if not exist.
found = client.bucket_exists("ikun")
if not found:
client.make_bucket("ikun")
else:
print("Bucket 'ikun' already exists")
client.fput_object(
"ikun", "ikun.mp4", "D:\Python_zjr\python_minio\ikun.mp4",
)
print(
"'D:\Python_zjr\python_minio\ikun.mp4' is successfully uploaded as "
"object 'ikun.mp4' to bucket 'ikun'."
)
if __name__ == "__main__":
try:
main()
except S3Error as exc:
print("error occurred.", exc)
进入play.min.io,在搜索框中可以看见成功的创建了名为ikun的桶。点击进去,也发现了已经成功的上传了我们的视频文件。
熟悉MinIO的API
首先,我们了解了 MinIO 的基本概念和常用的 API 操作,包括创建存储桶、上传对象、下载对象、删除对象等。然后,通过使用 Python 的 MinIO 客户端库,我们将这些 API 操作封装为函数,使其更加易于使用和维护。封装的函数包括创建存储桶、上传对象、下载对象、删除对象等功能,并提供了一些灵活的参数选项。
创建客户端
from minio import Minio
from minio.error import S3Error
minioClient = Minio(
endpoint='play.minio.io:9000',
access_key='Q3AM3UQ867SPQQA43P2F',
secret_key='zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG'
)
- endpoint:S3服务的主机名
- access_key:用户ID
- secret_key:密码
操作桶
1、检查桶是否存在,如果不存在就创建一个桶
def Foundbucket(client,bucket_name):
found = client.bucket_exists(bucket_name)
if not found:
client.make_bucket(bucket_name)
else:
print(f"Bucket {bucket_name} already exists")
2、列出所有的存储桶名
def get_bucket_list(client):
try:
buckets = client.list_buckets()
for bucket in buckets:
print(bucket.name, bucket.creation_date) # 获取桶的名称和创建时间
except InvalidResponseError as err:
print(err)
3、删除储存桶
def get_remove_bucket(client,bucket_name):
try:
client.remove_bucket(bucket_name)
print("删除存储桶成功")
except InvalidResponseError as err:
print(err)
4、用于查看存储桶的对象
def get_bucket_files(client,bucket_name):
try:
objects = client.list_objects(bucket_name, prefix=None,
recursive=True)
for obj in objects:
print(obj.bucket_name, obj.object_name.encode('utf-8'), obj.last_modified,
obj.etag, obj.size, obj.content_type)
except InvalidResponseError as err:
print(err)
操作对象
1、删除对象
def delete_object(client, bucket_name, objects):
try:
for obj in objects:
client.remove_object(bucket_name, obj)
print(f"Deleted {obj} under {bucket_name}")
except InvalidResponseError as err:
print(err)
2、删除zip文件
def delete_folder(client, bucket_name, folder_path):
try:
temp_dir = tempfile.mkdtemp()
folder_name = os.path.basename(folder_path)
zip_file = os.path.join(temp_dir, f"{folder_name}.zip")
shutil.make_archive(zip_file[:-4], 'zip', folder_path)
client.remove_object(bucket_name, f"{folder_name}.zip")
print(f"Deleted {folder_name}.zip from bucket {bucket_name}")
shutil.rmtree(temp_dir)
except InvalidResponseError as err:
print(err)
3、下载对象
def download_object(client, bucket_name, objects, filepath=None):
try:
if filepath is None:
current_dir = os.path.dirname(os.path.abspath(__file__)) # 获取当前文件的目录
else:
current_dir = filepath
for obj in objects:
file_path = os.path.join(current_dir, obj) # 拼接目录和文件名
client.fget_object(bucket_name, obj, file_path)
print(f"Downloaded object {obj} to {file_path}")
except InvalidResponseError as err:
print(err)
4、下载zip文件,自动解压
def download_folder(client, bucket_name, local_path):
try:
temp_dir = tempfile.mkdtemp()
client.fget_object(bucket_name, f"{local_path}.zip", os.path.join(temp_dir, f"{local_path}.zip"))
print(f"Downloaded {local_path}.zip from bucket {bucket_name}")
shutil.unpack_archive(os.path.join(temp_dir, f"{local_path}.zip"), local_path)
print(f"Extracted {local_path}.zip to {local_path}")
shutil.rmtree(temp_dir)
except InvalidResponseError as err:
print(err)
5、上传对象
def upload_object(client, bucket_name, file_path):
try:
object_name = os.path.basename(file_path) # 提取文件名作为对象名称
with open(file_path, "rb") as file_data:
file_size = os.path.getsize(file_path)
client.put_object(bucket_name, object_name, file_data, file_size)
print(f"Uploaded object {object_name} to bucket {bucket_name}")
except IOError as e:
print(f"Failed to open file: {file_path} - {e}")
except S3Error as err:
print(f"Error occurred: {err}")
6、上传文件夹为zip文件
def upload_folder(client, bucket_name, folder_path):
try:
temp_dir = tempfile.mkdtemp()
folder_name = os.path.basename(folder_path)
zip_file = os.path.join(temp_dir, f"{folder_name}.zip")
shutil.make_archive(zip_file[:-4], 'zip', folder_path)
with open(zip_file, "rb") as file_data:
file_size = os.path.getsize(zip_file)
client.put_object(bucket_name, f"{folder_name}.zip", file_data, file_size)
print(f"Uploaded {folder_name}.zip to bucket {bucket_name}")
shutil.rmtree(temp_dir)
except IOError as e:
print(f"Failed to compress folder: {folder_path} - {e}")
except S3Error as err:
print(f"Error occurred: {err}")
预签署
1、可供上传文件的URL,时限为2小时
def upload_url(self, client, bucket_name, object_name, expires_in=7200):
try:
# 生成预签名 URL
url = client.presigned_put_object(bucket_name, object_name, expires=timedelta(seconds=expires_in))
return url
except Exception as e:
print(f"Failed to generate presigned upload URL: {e}")
return None
2、可供下载文件的URL,时限为2小时
def download_url(client, bucket_name, object_name, expires_in=7200):
try:
# 生成预签名 URL
url = client.presigned_get_object(bucket_name, object_name, expires=timedelta(seconds=expires_in))
return url
except Exception as e:
print(f"Failed to generate presigned download URL: {e}")
return None
封装Minio的操作
第三方库 Pyzjr,它是一个开源的 Python 库。我将封装好的 MinIO API 函数上传到 Pyzjr 库中,可供我们团队轻松地使用这些函数,加速在 MinIO 上的开发工作。
1、先下载pyzjr第三方库
pip install pyzjr
版本大于等于0.0.9是有对minio的封装的。
2、UML图
下面是pyzjr中对minio的封装,与上面的函数相同。
想要对MinIO进行更多的操作的话,可以查看github文档,里面提供了很多写好的示例,如果想要进行操作,可以先去看看里面的示例代码进行修改。我这里封装的是我认为可能会用到的,不完整但足够使用了。
1.0.3之后pyzjr中的MinIO部分已经删除,安装之前的版本可见,下面是源码。
import os
import shutil
import tempfile
from datetime import timedelta
from minio import Minio
from minio.error import S3Error
from minio.error import InvalidResponseError
def Creatclient(endpoint,access_key,secret_key,mode=1):
"""
:param endpoint: S3服务的主机名
:param access_key: 用户ID
:param secret_key: 密码
:param mode: 默认为1,自定义,为0,表示使用游乐场测试与开发
:return: 返回创建的客户端
"""
if mode==0:
client = Minio(
endpoint="play.min.io",
access_key="Q3AM3UQ867SPQQA43P2F",
secret_key="zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG",
)
else:
client = Minio(
endpoint=endpoint,
access_key=access_key,
secret_key=secret_key,
)
return client
class Bucket():
"""操作桶"""
def Foundbucket(self,client,bucket_name):
"""
:param client: 客户端
:param bucket_name: 存储桶名
:return: 存在就继续进行,没有存在就创建一个桶
"""
found = client.bucket_exists(bucket_name)
if not found:
client.make_bucket(bucket_name)
else:
print(f"Bucket {bucket_name} already exists")
def get_bucket_list(self,client):
"""
列出所有的存储桶名
"""
try:
buckets = client.list_buckets()
for bucket in buckets:
print(bucket.name, bucket.creation_date) # 获取桶的名称和创建时间
except InvalidResponseError as err:
print(err)
def get_remove_bucket(self,client,bucket_name):
"""
删除存储桶
"""
try:
client.remove_bucket(bucket_name)
print("删除存储桶成功")
except InvalidResponseError as err:
print(err)
def get_bucket_files(self,client,bucket_name):
"""
用于查看存储桶的对象
"""
try:
objects = client.list_objects(bucket_name, prefix=None,
recursive=True)
for obj in objects:
print(obj.bucket_name, obj.object_name.encode('utf-8'), obj.last_modified,
obj.etag, obj.size, obj.content_type)
except InvalidResponseError as err:
print(err)
class Object():
"""操作对象
example:
MinIO3=MinIO.Object()
MinIO3.upload_object(client, "ikun", r'D:\Python_zjr\python_minio\doc\ipcs.docx')
MinIO3.upload_folder(client, "ikun","D:\Python_zjr\python_minio\doc")
MinIO3.delete_object(client, "ikun",["main2.py","MinIO.py"] )
MinIO3.delete_folder(client,"ikun","./doc")
MinIO3.download_folder(client, "ikun", r"folder")
MinIO.download_objects(client, "ikun",["main2.py","MinIO.py"] )
"""
def delete_object(self,client, bucket_name, objects):
"""
删除对象
:param client:
:param bucket_name:
:param objects: 列表,元素为字符,逗号为间隔,如['myobject-1', 'myobject-2', 'myobject-3']
:return:
"""
try:
for obj in objects:
client.remove_object(bucket_name, obj)
print(f"Deleted {obj} under {bucket_name}")
except InvalidResponseError as err:
print(err)
def download_object(self,client, bucket_name, objects, filepath=None):
"""
下载对象
:param client:
:param bucket_name:
:param objects: 列表,元素为字符,逗号为间隔,如["main2.py","MinIO.py"]
:param filepath: 默认为None,即是运行该文件的根目录下
:return:
"""
try:
if filepath is None:
current_dir = os.path.dirname(os.path.abspath(__file__)) # 获取当前文件的目录
else:
current_dir = filepath
for obj in objects:
file_path = os.path.join(current_dir, obj) # 拼接目录和文件名
client.fget_object(bucket_name, obj, file_path)
print(f"Downloaded object {obj} to {file_path}")
except InvalidResponseError as err:
print(err)
def upload_object(self,client, bucket_name, file_path):
"""
上传对象
:param client:
:param bucket_name:
:param file_path: 建议是绝对路径
:return:
"""
try:
object_name = os.path.basename(file_path) # 提取文件名作为对象名称
with open(file_path, "rb") as file_data:
file_size = os.path.getsize(file_path)
client.put_object(bucket_name, object_name, file_data, file_size)
print(f"Uploaded object {object_name} to bucket {bucket_name}")
except IOError as e:
print(f"Failed to open file: {file_path} - {e}")
except InvalidResponseError as err:
print(f"Error occurred: {err}")
def delete_folder(self,client, bucket_name, folder_path):
"""
删除的也是压缩包
:param client:
:param bucket_name:
:param folder_path:
:return:
"""
try:
temp_dir = tempfile.mkdtemp()
folder_name = os.path.basename(folder_path)
zip_file = os.path.join(temp_dir, f"{folder_name}.zip")
shutil.make_archive(zip_file[:-4], 'zip', folder_path)
client.remove_object(bucket_name, f"{folder_name}.zip")
print(f"Deleted {folder_name}.zip from bucket {bucket_name}")
shutil.rmtree(temp_dir)
except InvalidResponseError as err:
print(err)
def upload_folder(self,client, bucket_name, folder_path):
"""
上传的文件是zip压缩包
:param client:
:param bucket_name:
:param folder_path:
:return:
"""
try:
temp_dir = tempfile.mkdtemp()
folder_name = os.path.basename(folder_path)
zip_file = os.path.join(temp_dir, f"{folder_name}.zip")
shutil.make_archive(zip_file[:-4], 'zip', folder_path)
with open(zip_file, "rb") as file_data:
file_size = os.path.getsize(zip_file)
client.put_object(bucket_name, f"{folder_name}.zip", file_data, file_size)
print(f"Uploaded {folder_name}.zip to bucket {bucket_name}")
shutil.rmtree(temp_dir)
except IOError as e:
print(f"Failed to compress folder: {folder_path} - {e}")
except S3Error as err:
print(f"Error occurred: {err}")
def download_folder(self,client, bucket_name, local_path):
"""
下载后会自动解压
:param client:
:param bucket_name:
:param local_path:
:return:
"""
try:
temp_dir = tempfile.mkdtemp()
client.fget_object(bucket_name, f"{local_path}.zip", os.path.join(temp_dir, f"{local_path}.zip"))
print(f"Downloaded {local_path}.zip from bucket {bucket_name}")
shutil.unpack_archive(os.path.join(temp_dir, f"{local_path}.zip"), local_path)
print(f"Extracted {local_path}.zip to {local_path}")
shutil.rmtree(temp_dir)
except InvalidResponseError as err:
print(err)
class Presigned():
def upload_url(self, client, bucket_name, object_name, expires_in=7200):
"""
生成预签名的上传 URL
:param client: MinIO 客户端
:param bucket_name: 存储桶名称
:param object_name: 对象名称
:param expires_in: URL 的有效期(单位:秒),默认为 2 小时
:return: 预签名的上传 URL
"""
try:
# 生成预签名 URL
url = client.presigned_put_object(bucket_name, object_name, expires=timedelta(seconds=expires_in))
return url
except Exception as e:
print(f"Failed to generate presigned upload URL: {e}")
return None
def download_url(self,client, bucket_name, object_name, expires_in=7200):
"""
生成预签名的下载 URL
:param client: MinIO 客户端
:param bucket_name: 存储桶名称
:param object_name: 对象名称
:param expires_in: URL 的有效期(单位:秒),默认为 2 小时
:return: 预签名的下载 URL
"""
try:
# 生成预签名 URL
url = client.presigned_get_object(bucket_name, object_name, expires=timedelta(seconds=expires_in))
return url
except Exception as e:
print(f"Failed to generate presigned download URL: {e}")
return None
参考文章
Python的API参考文档(英文与中文):
Python Quickstart Guide — MinIO Object Storage for Linux
Minio SDKs - Python Client API文档 - 《Minio Cookbook 中文版》 - 书栈网 · BookStack