你可以在保存文件之前,对上传的图片进行压缩,以保证它的大小不超过 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 iorouter = APIRouter()MAX_FILE_SIZE_MB = 3
MAX_FILE_SIZE_BYTES = MAX_FILE_SIZE_MB * 1024 * 1024 # 3MBdef compress_image(image: Image.Image, format: str) -> bytes:"""压缩图片至 3MB 以内"""quality = 95while 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:breakquality -= 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.sizeif 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,从而满足接口要求。