#!/usr/bin/python # -*- coding: utf-8 -*- # @version : 1.0 # @Create Time : 2022/4/28 22:32 # @File : aliyun_oss.py # @IDE : PyCharm # @desc : 阿里云对象存储 import os.path from fastapi import UploadFile from pydantic import BaseModel import oss2 # 安装依赖库:pip install oss2 from oss2.models import PutObjectResult from core.exception import CustomException from core.logger import logger from utils import status from utils.file.file_manage import FileManage from utils.file.file_base import FileBase class BucketConf(BaseModel): accessKeyId: str accessKeySecret: str endpoint: str bucket: str baseUrl: str class AliyunOSS(FileBase): """ 阿里云对象存储 常见报错:https://help.aliyun.com/document_detail/185228.htm?spm=a2c4g.11186623.0.0.6de530e5pxNK76#concept-1957777 官方文档:https://help.aliyun.com/document_detail/32026.html 使用Python SDK时,大部分操作都是通过oss2.Service和oss2.Bucket两个类进行。 oss2.Service类用于列举存储空间。 oss2.Bucket类用于上传、下载、删除文件以及对存储空间进行各种配置。 """ def __init__(self, bucket: BucketConf): # 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。 auth = oss2.Auth(bucket.accessKeyId, bucket.accessKeySecret) # yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。 # 填写Bucket名称。 self.bucket = oss2.Bucket(auth, bucket.endpoint, bucket.bucket) self.baseUrl = bucket.baseUrl async def upload_image(self, path: str, file: UploadFile, max_size: int = 10) -> str: """ 上传图片 :param path: path由包含文件后缀,不包含Bucket名称组成的Object完整路径,例如abc/efg/123.jpg。 :param file: 文件对象 :param max_size: 图片文件最大值,单位 MB,默认 10MB :return: 上传后的文件oss链接 """ # 验证图片类型 await self.validate_file(file, max_size, self.IMAGE_ACCEPT) # 生成文件路径 path = self.generate_relative_path(path, file.filename) file_data = await file.read() return await self.__upload_file_to_oss(path, file_data) async def upload_video(self, path: str, file: UploadFile, max_size: int = 100) -> str: """ 上传视频 :param path: path由包含文件后缀,不包含Bucket名称组成的Object完整路径,例如abc/efg/123.jpg。 :param file: 文件对象 :param max_size: 视频文件最大值,单位 MB,默认 100MB :return: 上传后的文件oss链接 """ # 验证图片类型 await self.validate_file(file, max_size, self.VIDEO_ACCEPT) # 生成文件路径 path = self.generate_relative_path(path, file.filename) file_data = await file.read() return await self.__upload_file_to_oss(path, file_data) async def upload_file(self, path: str, file: UploadFile) -> str: """ 上传文件 :param path: path由包含文件后缀,不包含Bucket名称组成的Object完整路径,例如abc/efg/123.jpg。 :param file: 文件对象 :return: 上传后的文件oss链接 """ path = self.generate_relative_path(path, file.filename) file_data = await file.read() return await self.__upload_file_to_oss(path, file_data) async def __upload_file_to_oss(self, path: str, file_data: bytes) -> str: """ 上传文件到OSS :param path: path由包含文件后缀,不包含Bucket名称组成的Object完整路径,例如abc/efg/123.jpg。 :param file_data: 文件数据 :return: 上传后的文件oss链接 """ result = self.bucket.put_object(path, file_data) assert isinstance(result, PutObjectResult) if result.status != 200: logger.error(f"文件上传到OSS失败,状态码:{result.status}") raise CustomException("上传文件失败", code=status.HTTP_ERROR) return self.baseUrl + path