你可以在保存文件之前,对上传的图片进行压缩,以保证它的大小不超过 3 MB。可以使用 PIL
(Pillow)库来调整图片的尺寸和质量。这里是修改后的代码:
修改点
- 调整分辨率:如果图片大小超过 3 MB,则缩小分辨率。
- 调整压缩质量:JPEG 图片可以降低
quality
参数(如 85)。 - PNG 图片转换:PNG 无法直接调整质量,可以减少色深或调整分辨率。
修改后的代码
from fastapi import APIRouter, UploadFile, File, Form, HTTPException
from PIL import Image
import os
import io
router = APIRouter()
MAX_FILE_SIZE_MB = 3
MAX_FILE_SIZE_BYTES = MAX_FILE_SIZE_MB * 1024 * 1024 # 3MB
def compress_image(image: Image.Image, format: str) -> bytes:
"""压缩图片至 3MB 以内"""
quality = 95
while True:
img_byte_arr = io.BytesIO()
if format == "JPEG":
image.save(img_byte_arr, format="JPEG", quality=quality)
elif format == "PNG":
image.save(img_byte_arr, format="PNG", optimize=True)
else:
raise HTTPException(status_code=400, detail="Unsupported image format")
img_size = img_byte_arr.tell()
if img_size <= MAX_FILE_SIZE_BYTES or quality <= 20:
break
quality -= 5 # 逐步降低质量
return img_byte_arr.getvalue()
async def process_uploaded_image(file: UploadFile):
"""处理上传的图片,调整大小,压缩"""
if file.content_type not in ["image/jpeg", "image/png"]:
raise HTTPException(status_code=400, detail="Only JPEG and PNG images are allowed")
image = Image.open(io.BytesIO(await file.read()))
# 调整图片大小,如果太大则缩小
width, height = image.size
if width > 2000 or height > 2000:
image.thumbnail((2000, 2000)) # 先缩小到 2000x2000 以内
format = "JPEG" if file.content_type == "image/jpeg" else "PNG"
compressed_image_data = compress_image(image, format)
return file.filename, compressed_image_data
@router.post("/upload/face-photo")
async def upload_face_photo(file: UploadFile = File(...), phone: str = Form(..., description="用户手机号")):
"""上传手机拍摄的人脸照片,并进行压缩"""
filename, compressed_image_data = await process_uploaded_image(file)
file_path = os.path.join(settings.TEMP_DIR, filename)
with open(file_path, 'wb') as f:
f.write(compressed_image_data)
url = upload_to_oss(file_path, filename)
redis_key = f"face_photos:{
phone}"
redis_client.hset(redis_key, filename, url)
os.remove(file_path)
return {
"message": "Face photo uploaded successfully", "phone": phone, "filename": filename, "url": url}
@router.post("/upload/arm-photo")
async def upload_phone_photo(file: UploadFile = File(...), phone: str = Form(..., description="用户手机号")):
"""上传手机拍摄的手臂照片,并进行压缩"""
filename, compressed_image_data = await process_uploaded_image(file)
file_path = os.path.join(settings.TEMP_DIR, filename)
with open(file_path, 'wb') as f:
f.write(compressed_image_data)
url = upload_to_oss(file_path, filename)
redis_key = f"arm_photos:{
phone}"
redis_client.hset(redis_key, filename, url)
os.remove(file_path)
return {
"message": "Arm photo uploaded successfully", "phone": phone, "filename": filename, "url": url}
优化点
- 自动调整图片尺寸:如果图片尺寸过大,缩小至
2000x2000
以内。 - JPEG 逐步降低质量:从
95
逐步降低到20
,保证文件大小在3MB
以内。 - PNG 优化:对 PNG 进行优化,减少文件大小。
- 封装
process_uploaded_image
处理逻辑,复用代码,提高可读性。
这样,用户上传的超大图片会被自动调整大小和质量,不会超过 3MB,从而满足接口要求。