为啥要预签名
前端上传文件时无法直接和Minio通信,因为不可能把秘钥暴露在前端
预签名实现
前端把桶名称(query.bucket_name
)和对象名称(query.object_name
)传给后端,后端计算好上传URL后返回给前端,前端就可以使用URL直接上传文件到Minio中
注意:预签名URL是可以设置过期时间的——expires=timedelta(days=1)
@api.get("/presigned/url", summary="上传url预签名")
def presigned_url(query: ObjectQuery):
url = minio_client.presigned_put_object(query.bucket_name, query.object_name, expires=timedelta(days=1))
print(url)
return response(data=url)
为啥要分片上传
Minio允许上传的最大文件为5GB
预签名和分片结合
- 前端把桶名称(
query.bucket_name
)和对象名称(query.object_name
)传给后端获取上传ID - 前端计算文件的分片总数量,通过后端获取每个分片的上传列表
- 前端通过每个分片的上传列表直接将分片上传至Minio
- 分片合并
时序图:
接口实现:
@api.get("/upload/id", summary="获取分片上传ID")
def get_upload_id(query: MultipartUploadIDQuery):
upload_id = minio_client._create_multipart_upload(bucket_name=query.bucket_name, object_name=query.object_name,
headers={})
return response(data=upload_id)
@api.get("/multipart/presigned/urls", summary="获取分片上传url")
def multipart_presigned_urls(query: MultipartPresignedQuery):
url_list = []
for i in range(query.part_count):
url = minio_client.get_presigned_url(
"PUT",
query.bucket_name,
query.object_name,
expires=timedelta(days=1),
extra_query_params={"partNumber": str(i + 1), "uploadId": query.upload_id}
)
url_list.append(url)
return response(data=url_list)
@api.post("/complete/part", summary="合并分片")
def complete_part(body: CompletePartBody):
part_list = minio_client._list_parts(body.bucket_name, body.object_name, body.upload_id, max_parts=body.part_count)
print(part_list)
minio_client._complete_multipart_upload(body.bucket_name, body.object_name, body.upload_id, part_list.parts)
return response()
值得注意的是:max_parts的默认值为1000,如果分片总数超过1000,则需要手动指定
总结
- 预签名上传简化了上传流程,将
前端→后端→对象存储
转化为前端→对象存储
,节省了上传带宽 - 预签名与分片上传相结合,解决了大文件上传限制,并利用Minio分片合并机制大大加快了分片合并的速度