기능 개선 — 사진 업로드, HEIC 지원, 재조치 흐름, 신고 순번, 모바일 UI
- 이미지 압축: 삼성/네이버 브라우저 호환, URL.createObjectURL 방식으로 메모리 절감, 대용량 PNG/HEIC 처리, blob 유효성 검증, 순차 압축으로 모바일 OOM 방지 - HEIC/HEIF 지원: pillow-heif 서버사이드 변환, Pillow 12.2.0 업그레이드 - 조치 페이지: '조치 완료 저장' 단일 버튼으로 단순화 - 재조치 흐름: 관리자 재조치 요청 시 이전 조치 이력을 번호 카드로 순차 표시 - 신고 순번: 전체 기준 ROW_NUMBER(oldest=1) 순번 표시, 삭제 gap 제거 - 모바일 탭바: position:fixed 적용으로 nav 하단 흰 여백 제거 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,18 +1,47 @@
|
||||
import os, uuid, qrcode
|
||||
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 기준 상대 경로 반환"""
|
||||
"""파일을 저장하고 /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(file.file.read())
|
||||
f.write(raw)
|
||||
return f"/uploads/{sub_dir}/{filename}"
|
||||
|
||||
def generate_qr(charger_id: str, domain: str) -> str:
|
||||
|
||||
Reference in New Issue
Block a user