"""生成并缓存缩略图。""" from __future__ import annotations import hashlib from pathlib import Path from PIL import Image from app.core.config import settings from app.core.path_utils import path_to_storage SUPPORTED_EXTS = {".png", ".jpg", ".jpeg", ".webp", ".gif", ".bmp", ".tif", ".tiff"} def is_supported(path: Path) -> bool: """是否为我们支持的图片格式。""" return path.suffix.lower() in SUPPORTED_EXTS def generate_thumbnail(image_path: Path, max_side: int | None = None) -> Path: """生成 webp 缩略图,落到缓存目录。返回缓存路径。""" max_side = max_side or settings.thumb_size # 用文件路径 + mtime 哈希作为缓存键,源文件变化会自动生成新缩略图 stat = image_path.stat() key = hashlib.md5( f"{path_to_storage(image_path)}|{stat.st_mtime_ns}|{max_side}".encode("utf-8") ).hexdigest() out = settings.thumb_dir / f"{key}.webp" if out.exists(): return out with Image.open(image_path) as img: img = img.convert("RGB") img.thumbnail((max_side, max_side), Image.LANCZOS) img.save(out, format="WEBP", quality=80) return out def file_hash(image_path: Path, chunk: int = 1024 * 1024) -> str: """计算文件 sha256,用作去重键。""" h = hashlib.sha256() with open(image_path, "rb") as f: while True: data = f.read(chunk) if not data: break h.update(data) return h.hexdigest()