Spaces:
Running
Running
| # Dependencies | |
| import magic | |
| from PIL import Image | |
| from pathlib import Path | |
| from typing import Tuple | |
| from utils.logger import get_logger | |
| from config.settings import settings | |
| from config.constants import MIN_IMAGE_DIMENSION | |
| from config.constants import MAX_IMAGE_DIMENSION | |
| # Setup Logging | |
| logger = get_logger(__name__) | |
| class ValidationError(Exception): | |
| """ | |
| Custom validation error | |
| """ | |
| pass | |
| class ImageValidator: | |
| """ | |
| Validate uploaded images | |
| """ | |
| def validate_file_size(file_size: int) -> None: | |
| """ | |
| Validate file size | |
| """ | |
| if (file_size > settings.max_file_size_bytes): | |
| raise ValidationError(f"File size {file_size} bytes exceeds maximum {settings.max_file_size_bytes} bytes") | |
| if (file_size == 0): | |
| raise ValidationError("File is empty") | |
| def validate_file_extension(filename: str) -> None: | |
| """ | |
| Validate file extension | |
| """ | |
| extension = Path(filename).suffix.lower() | |
| if (extension not in settings.ALLOWED_EXTENSIONS): | |
| raise ValidationError(f"File extension {extension} not allowed. Allowed: {', '.join(settings.ALLOWED_EXTENSIONS)}") | |
| def validate_image_content(file_path: Path) -> Tuple[int, int]: | |
| """ | |
| Validate image can be opened and get dimensions | |
| """ | |
| try: | |
| with Image.open(file_path) as image: | |
| width, height = image.size | |
| # Validate dimensions | |
| if ((width < MIN_IMAGE_DIMENSION) or (height < MIN_IMAGE_DIMENSION)): | |
| raise ValidationError(f"Image dimensions ({width}x{height}) too small. Minimum: {MIN_IMAGE_DIMENSION}px") | |
| if ((width > MAX_IMAGE_DIMENSION) or (height > MAX_IMAGE_DIMENSION)): | |
| raise ValidationError(f"Image dimensions ({width}x{height}) too large. Maximum: {MAX_IMAGE_DIMENSION}px") | |
| # Verify format | |
| if (image.format.lower() not in ['jpeg', 'png', 'webp']): | |
| raise ValidationError(f"Unsupported image format: {image.format}") | |
| return width, height | |
| except ValidationError: | |
| raise | |
| except Exception as e: | |
| raise ValidationError(f"Cannot open image: {str(e)}") | |
| def validate_mime_type(file_path: Path) -> None: | |
| """ | |
| Validate MIME type matches image | |
| """ | |
| try: | |
| mime = magic.from_file(str(file_path), mime = True) | |
| if (not mime.startswith('image/')): | |
| raise ValidationError(f"File is not an image. MIME type: {mime}") | |
| except Exception as e: | |
| logger.warning(f"MIME type validation failed: {e}") | |
| # Don't fail if python-magic is not available | |
| def validate_image(cls, file_path: Path, filename: str, file_size: int) -> Tuple[int, int]: | |
| """ | |
| Comprehensive image validation | |
| """ | |
| cls.validate_file_size(file_size) | |
| cls.validate_file_extension(filename) | |
| dimensions = cls.validate_image_content(file_path) | |
| cls.validate_mime_type(file_path) # Optional, commented out if python-magic not available | |
| logger.debug(f"Validated image: {filename} ({dimensions[0]}x{dimensions[1]})") | |
| return dimensions |