对上传的图片进行压缩,以保证它的大小不超过X MB

你可以在保存文件之前,对上传的图片进行压缩,以保证它的大小不超过 3 MB。可以使用 PIL(Pillow)库来调整图片的尺寸和质量。这里是修改后的代码:

修改点

  1. 调整分辨率:如果图片大小超过 3 MB,则缩小分辨率。
  2. 调整压缩质量:JPEG 图片可以降低 quality 参数(如 85)。
  3. 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}

优化点

  1. 自动调整图片尺寸:如果图片尺寸过大,缩小至 2000x2000 以内。
  2. JPEG 逐步降低质量:从 95 逐步降低到 20,保证文件大小在 3MB 以内。
  3. PNG 优化:对 PNG 进行优化,减少文件大小。
  4. 封装 process_uploaded_image 处理逻辑,复用代码,提高可读性。

这样,用户上传的超大图片会被自动调整大小和质量,不会超过 3MB,从而满足接口要求。

猜你喜欢

转载自blog.csdn.net/sunyuhua_keyboard/article/details/146382289