Spaces:
Sleeping
Sleeping
Commit ·
b434fc2
1
Parent(s): 1ebea81
Normalize uploaded photos' EXIF orientation
Browse filesAdd image normalization for uploaded photos using Pillow. Import Image, ImageOps and UnidentifiedImageError; introduce EXIF_NORMALIZE_EXTS and a _normalize_uploaded_photo(path) helper that applies ImageOps.exif_transpose, converts incompatible modes for JPEG, and saves with reasonable JPEG options. Call the normalizer for files categorized as "photos" and update the stored file size afterward. Errors decoding the image are caught and the original bytes are preserved.
server/app/services/session_store.py
CHANGED
|
@@ -11,6 +11,7 @@ from typing import Iterable, List, Optional
|
|
| 11 |
from uuid import uuid4
|
| 12 |
|
| 13 |
from fastapi import UploadFile
|
|
|
|
| 14 |
|
| 15 |
from ..core.config import get_settings
|
| 16 |
|
|
@@ -18,6 +19,7 @@ from ..core.config import get_settings
|
|
| 18 |
IMAGE_EXTS = {".jpg", ".jpeg", ".png", ".gif", ".webp"}
|
| 19 |
DOC_EXTS = {".pdf", ".doc", ".docx"}
|
| 20 |
DATA_EXTS = {".csv", ".xls", ".xlsx"}
|
|
|
|
| 21 |
SESSION_ID_RE = re.compile(r"^[0-9a-f]{32}$")
|
| 22 |
BUILTIN_PAGE_TEMPLATES = [
|
| 23 |
{
|
|
@@ -82,6 +84,27 @@ def _category_for(filename: str) -> str:
|
|
| 82 |
return "documents"
|
| 83 |
|
| 84 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
def _validate_session_id(session_id: str) -> str:
|
| 86 |
if not session_id:
|
| 87 |
raise ValueError("Invalid session id.")
|
|
@@ -585,6 +608,9 @@ class SessionStore:
|
|
| 585 |
handle.write(chunk)
|
| 586 |
|
| 587 |
category = _category_for(filename)
|
|
|
|
|
|
|
|
|
|
| 588 |
return StoredFile(
|
| 589 |
id=file_id,
|
| 590 |
name=filename,
|
|
|
|
| 11 |
from uuid import uuid4
|
| 12 |
|
| 13 |
from fastapi import UploadFile
|
| 14 |
+
from PIL import Image, ImageOps, UnidentifiedImageError
|
| 15 |
|
| 16 |
from ..core.config import get_settings
|
| 17 |
|
|
|
|
| 19 |
IMAGE_EXTS = {".jpg", ".jpeg", ".png", ".gif", ".webp"}
|
| 20 |
DOC_EXTS = {".pdf", ".doc", ".docx"}
|
| 21 |
DATA_EXTS = {".csv", ".xls", ".xlsx"}
|
| 22 |
+
EXIF_NORMALIZE_EXTS = {".jpg", ".jpeg", ".png", ".webp"}
|
| 23 |
SESSION_ID_RE = re.compile(r"^[0-9a-f]{32}$")
|
| 24 |
BUILTIN_PAGE_TEMPLATES = [
|
| 25 |
{
|
|
|
|
| 84 |
return "documents"
|
| 85 |
|
| 86 |
|
| 87 |
+
def _normalize_uploaded_photo(path: Path) -> None:
|
| 88 |
+
ext = path.suffix.lower()
|
| 89 |
+
if ext not in EXIF_NORMALIZE_EXTS:
|
| 90 |
+
return
|
| 91 |
+
|
| 92 |
+
try:
|
| 93 |
+
with Image.open(path) as image:
|
| 94 |
+
normalized = ImageOps.exif_transpose(image)
|
| 95 |
+
format_name = image.format
|
| 96 |
+
if normalized.mode in ("RGBA", "LA", "P") and format_name in {"JPEG", "JPG"}:
|
| 97 |
+
normalized = normalized.convert("RGB")
|
| 98 |
+
save_kwargs = {"exif": b""}
|
| 99 |
+
if format_name in {"JPEG", "JPG"}:
|
| 100 |
+
save_kwargs["quality"] = 95
|
| 101 |
+
save_kwargs["optimize"] = True
|
| 102 |
+
normalized.save(path, format=format_name, **save_kwargs)
|
| 103 |
+
except (UnidentifiedImageError, OSError, ValueError, TypeError):
|
| 104 |
+
# Keep original bytes if the file cannot be decoded by Pillow.
|
| 105 |
+
return
|
| 106 |
+
|
| 107 |
+
|
| 108 |
def _validate_session_id(session_id: str) -> str:
|
| 109 |
if not session_id:
|
| 110 |
raise ValueError("Invalid session id.")
|
|
|
|
| 608 |
handle.write(chunk)
|
| 609 |
|
| 610 |
category = _category_for(filename)
|
| 611 |
+
if category == "photos":
|
| 612 |
+
_normalize_uploaded_photo(dest)
|
| 613 |
+
size = dest.stat().st_size
|
| 614 |
return StoredFile(
|
| 615 |
id=file_id,
|
| 616 |
name=filename,
|