jiachenlong/backend/app/services/oss.py

127 lines
3.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 阿里云OSS服务
import os
import uuid
import datetime
from typing import Optional
import oss2
from PIL import Image
import io
# OSS配置
OSS_CONFIG = {
"access_key_id": os.getenv("OSS_ACCESS_KEY_ID", "LTAI5t6HUnpFBLEK9194kPVG"),
"access_key_secret": os.getenv("OSS_ACCESS_KEY_SECRET", "LEr4Q8yRxb8D5b24cKfCwlt4MMoke1"),
"bucket_name": "jiachenlong-oss",
"endpoint": "oss-cn-hangzhou.aliyuncs.com",
"public_url": "https://jiachenlong-oss.oss-cn-hangzhou.aliyuncs.com"
}
# 图片压缩配置
IMAGE_CONFIG = {
"max_size": 1024 * 1024, # 1MB
"max_width": 2048,
"max_height": 2048,
"quality": 85,
"format": "JPEG"
}
# 初始化OSS
auth = oss2.Auth(OSS_CONFIG["access_key_id"], OSS_CONFIG["access_key_secret"])
bucket = oss2.Bucket(auth, OSS_CONFIG["endpoint"], OSS_CONFIG["bucket_name"])
def get_oss_path(prefix: str, user_id: str = None, filename: str = None) -> str:
"""生成OSS存储路径"""
now = datetime.datetime.now()
year = now.strftime("%Y")
month = now.strftime("%m")
day = now.strftime("%d")
if filename:
ext = filename.split('.')[-1] if '.' in filename else 'jpg'
unique_name = f"{uuid.uuid4().hex}.{ext}"
else:
unique_name = f"{uuid.uuid4().hex}.jpg"
if user_id:
path = f"{prefix}/{user_id}/{year}/{month}/{unique_name}"
else:
path = f"{prefix}/{year}/{month}/{day}/{unique_name}"
return path, unique_name
def upload_to_oss(file_data: bytes, oss_key: str, compress: bool = True) -> str:
"""上传文件到OSS返回公网URL"""
try:
# 如果是图片,进行压缩
if compress and any(oss_key.lower().endswith(ext) for ext in ['.jpg', '.jpeg', '.png', '.webp']):
file_data = compress_image(file_data)
# 上传文件
result = bucket.put_object(oss_key, file_data)
if result.status == 200:
# 返回公网URL
return f"{OSS_CONFIG['public_url']}/{oss_key}"
else:
raise Exception(f"OSS上传失败: {result.status}")
except Exception as e:
raise Exception(f"OSS上传失败: {str(e)}")
def delete_from_oss(oss_key: str) -> bool:
"""从OSS删除文件"""
try:
result = bucket.delete_object(oss_key)
return result.status == 204
except Exception as e:
print(f"OSS删除失败: {str(e)}")
return False
def get_public_url(oss_key: str) -> str:
"""获取公网URL"""
return f"{OSS_CONFIG['public_url']}/{oss_key}"
def compress_image(image_data: bytes, max_size: int = None) -> bytes:
"""压缩图片到指定大小以内"""
if max_size is None:
max_size = IMAGE_CONFIG["max_size"]
# 如果已经小于限制,直接返回
if len(image_data) <= max_size:
return image_data
# 打开图片
img = Image.open(io.BytesIO(image_data))
# 如果是PNG且有透明通道转换为RGB
if img.mode in ('RGBA', 'P'):
img = img.convert('RGB')
# 逐步降低质量直到达到目标大小
quality = 95
compressed_data = image_data
while quality > 30 and len(compressed_data) > max_size:
output = io.BytesIO()
img.save(output, format=IMAGE_CONFIG["format"], quality=quality, optimize=True)
compressed_data = output.getvalue()
quality -= 10
# 如果还是太大,缩小尺寸
if len(compressed_data) > max_size:
width, height = img.size
while len(compressed_data) > max_size and width > 400:
width = int(width * 0.8)
height = int(height * 0.8)
img_resized = img.resize((width, height), Image.Resampling.LANCZOS)
output = io.BytesIO()
img_resized.save(output, format=IMAGE_CONFIG["format"], quality=80, optimize=True)
compressed_data = output.getvalue()
return compressed_data