diff --git a/system3-nonconformance/api/Dockerfile b/system3-nonconformance/api/Dockerfile index fdb7697..f326cde 100644 --- a/system3-nonconformance/api/Dockerfile +++ b/system3-nonconformance/api/Dockerfile @@ -2,11 +2,16 @@ FROM python:3.11-slim WORKDIR /app -# 시스템 패키지 설치 (gosu: entrypoint 에서 root→appuser 강등용) +# 시스템 패키지 설치 +# - gosu: entrypoint 에서 root→appuser 강등용 +# - imagemagick + libheif: HEIC(iPhone) 등 pillow_heif 가 처리 못하는 이미지 fallback (2026-04-09) RUN apt-get update && apt-get install -y \ gcc \ gosu \ - && rm -rf /var/lib/apt/lists/* + imagemagick \ + libheif1 \ + && rm -rf /var/lib/apt/lists/* \ + && sed -i 's|||' /etc/ImageMagick-6/policy.xml 2>/dev/null || true # Python 의존성 설치 COPY requirements.txt . diff --git a/system3-nonconformance/api/services/file_service.py b/system3-nonconformance/api/services/file_service.py index 5308029..fbb39d3 100644 --- a/system3-nonconformance/api/services/file_service.py +++ b/system3-nonconformance/api/services/file_service.py @@ -84,11 +84,48 @@ def save_base64_image(base64_string: str, prefix: str = "image") -> Optional[str print(f"✅ HEIF 재시도 성공: {image.format}, 모드: {image.mode}, 크기: {image.size}") except Exception as heif_e: print(f"❌ HEIF 처리도 실패: {heif_e}") - print("❌ 지원되지 않는 이미지 형식") - raise e + image = None # ImageMagick fallback 시도 else: print("❌ HEIF 지원 라이브러리가 설치되지 않거나 처리 불가") - raise e + + # ImageMagick fallback (pillow_heif 가 특정 iPhone HEIC 메타데이터를 처리 못하는 경우) + if image is None: + print("🔄 ImageMagick 으로 fallback 시도...") + try: + import subprocess + import tempfile + with tempfile.NamedTemporaryFile(suffix='.bin', delete=False, dir='/tmp') as tmp_in: + tmp_in.write(image_data) + tmp_in_path = tmp_in.name + tmp_out_path = tmp_in_path + '.jpg' + # convert 는 ImageMagick 6, magick 은 ImageMagick 7 + cmd = None + for binary in ('magick', 'convert'): + try: + subprocess.run([binary, '-version'], capture_output=True, timeout=5, check=True) + cmd = binary + break + except (FileNotFoundError, subprocess.CalledProcessError, subprocess.TimeoutExpired): + continue + if not cmd: + raise Exception("ImageMagick not installed") + result = subprocess.run( + [cmd, tmp_in_path, tmp_out_path], + capture_output=True, timeout=30, text=True + ) + if result.returncode != 0: + raise Exception(f"ImageMagick 변환 실패: {result.stderr[:200]}") + image = Image.open(tmp_out_path) + image.load() # 파일 삭제 전 강제 로드 + print(f"✅ ImageMagick fallback 성공: {image.format}, 모드: {image.mode}, 크기: {image.size}") + try: + os.unlink(tmp_in_path) + os.unlink(tmp_out_path) + except OSError: + pass + except Exception as magick_e: + print(f"❌ ImageMagick fallback 실패: {magick_e}") + raise e # 원래 PIL 에러를 올림 # 이미지가 성공적으로 로드되지 않은 경우 if image is None: