add image flow
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
from dataclasses import dataclass
|
||||
from io import BytesIO
|
||||
|
||||
from fastapi import HTTPException, UploadFile
|
||||
from PIL import Image, UnidentifiedImageError
|
||||
|
||||
|
||||
MAX_IMAGE_SIDE = 1600
|
||||
JPEG_QUALITY = 80
|
||||
JPEG_MIME_TYPE = "image/jpeg"
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class ProcessedImage:
|
||||
blob: bytes
|
||||
mime_type: str
|
||||
width: int
|
||||
height: int
|
||||
|
||||
|
||||
def process_upload(file: UploadFile | None) -> ProcessedImage | None:
|
||||
if file is None or not file.filename:
|
||||
return None
|
||||
|
||||
try:
|
||||
raw_bytes = file.file.read()
|
||||
if not raw_bytes:
|
||||
raise HTTPException(status_code=400, detail="上传的图片内容为空")
|
||||
|
||||
with Image.open(BytesIO(raw_bytes)) as source_image:
|
||||
processed_image = _prepare_image(source_image)
|
||||
except UnidentifiedImageError as exc:
|
||||
raise HTTPException(status_code=400, detail="上传的文件不是合法图片") from exc
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as exc:
|
||||
raise HTTPException(status_code=400, detail="图片处理失败,请尝试更换图片") from exc
|
||||
finally:
|
||||
file.file.close()
|
||||
|
||||
return processed_image
|
||||
|
||||
|
||||
def _prepare_image(source_image: Image.Image) -> ProcessedImage:
|
||||
prepared = _strip_metadata_and_convert(source_image)
|
||||
prepared.thumbnail((MAX_IMAGE_SIDE, MAX_IMAGE_SIDE))
|
||||
|
||||
output = BytesIO()
|
||||
prepared.save(output, format="JPEG", quality=JPEG_QUALITY, optimize=True)
|
||||
blob = output.getvalue()
|
||||
|
||||
return ProcessedImage(
|
||||
blob=blob,
|
||||
mime_type=JPEG_MIME_TYPE,
|
||||
width=prepared.width,
|
||||
height=prepared.height,
|
||||
)
|
||||
|
||||
|
||||
def _strip_metadata_and_convert(source_image: Image.Image) -> Image.Image:
|
||||
if source_image.mode in ("RGBA", "LA"):
|
||||
background = Image.new("RGB", source_image.size, (255, 255, 255))
|
||||
alpha = source_image.getchannel("A")
|
||||
background.paste(source_image.convert("RGBA"), mask=alpha)
|
||||
return background
|
||||
|
||||
if source_image.mode == "P":
|
||||
return source_image.convert("RGB")
|
||||
|
||||
if source_image.mode != "RGB":
|
||||
return source_image.convert("RGB")
|
||||
|
||||
return source_image.copy()
|
||||
Reference in New Issue
Block a user