import os, uuid, io, qrcode from PIL import Image from fastapi import UploadFile # HEIC/HEIF 지원 등록 (pillow-heif) try: from pillow_heif import register_heif_opener register_heif_opener() _HEIF_SUPPORTED = True except ImportError: _HEIF_SUPPORTED = False UPLOAD_DIR = os.getenv("UPLOAD_DIR", "/uploads") # 브라우저에서 표시 불가능한 포맷 → 서버에서 JPEG 변환 _CONVERT_EXTS = {".heic", ".heif", ".avif"} def save_upload(file: UploadFile, sub_dir: str = "general") -> str: """파일을 저장하고 /uploads 기준 상대 경로 반환. HEIC/HEIF 등 브라우저 비호환 포맷은 JPEG로 변환 후 저장.""" raw = file.file.read() ext = os.path.splitext(file.filename or "file")[1].lower() or ".jpg" folder = os.path.join(UPLOAD_DIR, sub_dir) os.makedirs(folder, exist_ok=True) if ext in _CONVERT_EXTS: # HEIC/HEIF → JPEG 변환 try: img = Image.open(io.BytesIO(raw)) if img.mode in ("RGBA", "P", "LA"): img = img.convert("RGB") elif img.mode != "RGB": img = img.convert("RGB") buf = io.BytesIO() img.save(buf, format="JPEG", quality=88, optimize=True) raw = buf.getvalue() ext = ".jpg" except Exception: pass # 변환 실패 시 원본 그대로 저장 filename = f"{uuid.uuid4().hex}{ext}" filepath = os.path.join(folder, filename) with open(filepath, "wb") as f: f.write(raw) return f"/uploads/{sub_dir}/{filename}" def generate_qr(charger_id: str, domain: str) -> str: """QR 이미지를 저장하고 /uploads 기준 경로 반환""" url = f"https://{domain}/report/{charger_id}" qr = qrcode.QRCode(version=1, box_size=10, border=4) qr.add_data(url) qr.make(fit=True) img = qr.make_image(fill_color="black", back_color="white") folder = os.path.join(UPLOAD_DIR, "qr") os.makedirs(folder, exist_ok=True) filepath = os.path.join(folder, f"{charger_id}.png") img.save(filepath) return f"/uploads/qr/{charger_id}.png"