Spaces:
Running
Running
File size: 3,934 Bytes
1e4fc28 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
"""
Image storage utilities for saving and serving uploaded images.
"""
import os
import uuid
import shutil
from pathlib import Path
from typing import Optional, Tuple
from werkzeug.utils import secure_filename
def ensure_images_dir(images_dir: str) -> str:
"""Ensure images directory exists and return its path."""
os.makedirs(images_dir, exist_ok=True)
return images_dir
def generate_unique_filename(original_filename: str) -> str:
"""
Generate a unique filename to avoid collisions.
Format: {uuid}_{secure_original_name} or just {uuid}.jpg if original is invalid
"""
# Get secure base name
base_name = secure_filename(original_filename)
if not base_name:
base_name = "upload.jpg"
# Add UUID prefix for uniqueness (use full UUID to ensure uniqueness)
name, ext = os.path.splitext(base_name)
if not ext or ext.lower() not in ('.jpg', '.jpeg', '.png'):
ext = '.jpg'
unique_id = str(uuid.uuid4()) # Full UUID for better uniqueness
return f"{unique_id}_{name}{ext}"
def save_image(source_path: str, images_dir: str, original_filename: str) -> Optional[str]:
"""
Save an image from source_path to images_dir with a unique filename.
Args:
source_path: Path to source image file
images_dir: Directory to save images to
original_filename: Original filename for reference
Returns:
Stored filename (relative to images_dir) or None on failure
"""
try:
ensure_images_dir(images_dir)
# Generate unique filename
stored_filename = generate_unique_filename(original_filename)
dest_path = os.path.join(images_dir, stored_filename)
# Copy file
shutil.copy2(source_path, dest_path)
return stored_filename
except Exception as e:
# Log error but don't fail the request
import logging
logging.getLogger(__name__).exception(f"Failed to save image: {e}")
return None
def get_image_path(images_dir: str, filename: str) -> Optional[str]:
"""
Get full path to an image file if it exists.
Args:
images_dir: Base images directory
filename: Image filename
Returns:
Full path to image or None if not found
"""
if not filename:
return None
# Security: ensure filename doesn't contain path traversal
# Extract just the basename to prevent directory traversal
base_filename = os.path.basename(filename)
safe_filename = secure_filename(base_filename)
if not safe_filename:
return None
# Use safe_filename for the path (secure_filename may have sanitized it)
# But also try the original if it's already safe
image_path = os.path.join(images_dir, safe_filename)
if os.path.exists(image_path) and os.path.isfile(image_path):
return image_path
# Also try the original filename if it's different and seems safe
if safe_filename != base_filename:
# Check if original is safe (no path separators, no parent dir references)
if base_filename == filename and '/' not in base_filename and '\\' not in base_filename and '..' not in base_filename:
alt_path = os.path.join(images_dir, base_filename)
if os.path.exists(alt_path) and os.path.isfile(alt_path):
return alt_path
return None
def delete_image(images_dir: str, filename: str) -> bool:
"""
Delete an image file.
Args:
images_dir: Base images directory
filename: Image filename to delete
Returns:
True if deleted, False otherwise
"""
try:
image_path = get_image_path(images_dir, filename)
if image_path and os.path.exists(image_path):
os.remove(image_path)
return True
return False
except Exception:
return False
|