From 3dbd554eeb10c7418741ea6e2d44c6f0059f9bed Mon Sep 17 00:00:00 2001 From: Gilles Soulier Date: Mon, 25 May 2026 13:29:45 +0200 Subject: [PATCH] =?UTF-8?q?fix(media):=20upload=20photo=20=E2=80=94=20tail?= =?UTF-8?q?le,=20formats=20et=20nginx?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - nginx : client_max_body_size 15m (photos smartphone > 1 Mo rejetées silencieusement) - backend : redimensionnement original à 500×500 max (aspect ratio conservé) avant sauvegarde WEBP - backend : thumbnail généré depuis l'image déjà redimensionnée (économie mémoire) - backend : formats acceptés étendus — image/heic, image/heif, image/jpg - backend : normalisation content-type en lowercase (robustesse navigateurs) Co-Authored-By: Claude Sonnet 4.6 --- backend/app/api/media.py | 7 ++++--- backend/app/services/media.py | 26 ++++++++++++++++++-------- frontend/nginx.conf | 2 ++ 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/backend/app/api/media.py b/backend/app/api/media.py index 6222318..1eb54e1 100644 --- a/backend/app/api/media.py +++ b/backend/app/api/media.py @@ -12,12 +12,13 @@ async def upload_media( file: UploadFile = File(...), context: str = Query(default="note", pattern="^(product|note|attachment)$"), ): - if file.content_type in ALLOWED_IMAGE_TYPES: + content_type = (file.content_type or "").lower() + if content_type in ALLOWED_IMAGE_TYPES: result = await save_image(file, context=context) - elif file.content_type in ALLOWED_AUDIO_TYPES: + elif content_type in ALLOWED_AUDIO_TYPES: result = await save_audio(file) else: - raise HTTPException(status_code=400, detail=f"Type de fichier non supporté : {file.content_type}") + raise HTTPException(status_code=400, detail=f"Type de fichier non supporté : {content_type}") return MediaUploadResponse(**result) diff --git a/backend/app/services/media.py b/backend/app/services/media.py index 35fa5b7..4872e7b 100644 --- a/backend/app/services/media.py +++ b/backend/app/services/media.py @@ -9,9 +9,14 @@ from app.core.config import settings UPLOAD_DIR: Path = settings.upload_path -ALLOWED_IMAGE_TYPES = {"image/jpeg", "image/png", "image/webp", "image/svg+xml"} +ALLOWED_IMAGE_TYPES = { + "image/jpeg", "image/jpg", "image/png", "image/webp", "image/svg+xml", + "image/heic", "image/heif", +} ALLOWED_AUDIO_TYPES = {"audio/webm", "audio/mp4"} +MAX_ORIG_SIZE = (500, 500) + THUMBNAIL_SIZES = { "product": (150, 150), "note": (300, 300), @@ -20,17 +25,18 @@ THUMBNAIL_SIZES = { async def save_image(file: UploadFile, context: str = "note") -> dict: - if file.content_type not in ALLOWED_IMAGE_TYPES: - raise HTTPException(status_code=400, detail=f"Format non supporté : {file.content_type}") + content_type = (file.content_type or "").lower() + if content_type not in ALLOWED_IMAGE_TYPES: + raise HTTPException(status_code=400, detail=f"Format non supporté : {content_type}") file_id = str(uuid.uuid4()) content = await file.read() orig_dir = UPLOAD_DIR / "images" / "originals" orig_dir.mkdir(parents=True, exist_ok=True) - orig_path = orig_dir / f"{file_id}.webp" - if file.content_type == "image/svg+xml": + if content_type == "image/svg+xml": + orig_path = orig_dir / f"{file_id}.svg" orig_path.write_bytes(content) return { "file_id": file_id, @@ -39,16 +45,20 @@ async def save_image(file: UploadFile, context: str = "note") -> dict: "file_type": "image", } + orig_path = orig_dir / f"{file_id}.webp" img = Image.open(io.BytesIO(content)).convert("RGB") + + # Redimensionne l'original à 500×500 max en conservant l'aspect ratio + img.thumbnail(MAX_ORIG_SIZE, Image.LANCZOS) img.save(orig_path, "WEBP", quality=85) thumb_dir = UPLOAD_DIR / "images" / "thumbnails" thumb_dir.mkdir(parents=True, exist_ok=True) thumb_path = thumb_dir / f"{file_id}_thumb.webp" - size = THUMBNAIL_SIZES.get(context, (300, 300)) - img_thumb = Image.open(io.BytesIO(content)).convert("RGB") - img_thumb.thumbnail(size, Image.LANCZOS) + thumb_size = THUMBNAIL_SIZES.get(context, (300, 300)) + img_thumb = img.copy() + img_thumb.thumbnail(thumb_size, Image.LANCZOS) img_thumb.save(thumb_path, "WEBP", quality=80) return { diff --git a/frontend/nginx.conf b/frontend/nginx.conf index 3bffdda..93f5204 100644 --- a/frontend/nginx.conf +++ b/frontend/nginx.conf @@ -4,6 +4,8 @@ server { root /usr/share/nginx/html; index index.html; + client_max_body_size 15m; + gzip on; gzip_types text/plain text/css application/json application/javascript text/xml application/xml image/svg+xml;