오늘은 utils.py 코드 해석을 해보겠다
아래 내용은 정말 사소한 개념을 정리해 놓은 것이니 참고용으로 보는게 좋을 것 같다
# app/utils.py
import os
import uuid
import io
import base64
import logging
import os
- 파일 복사,디렉토리 생성 등
import uuid
- 고유 ID를 만들기 위한 모듈
import io
- 텍스트 및 바이너리 데이터의 스트림 처리를 위해 사용하는 모듈
스트림 처리
: 데이터가 생성되는 즉시 처리하여 실시간으로 결과를 얻는 데이터 처리 방식
import base64
- Base64 인코딩 및 디코딩을 수행할 수 있음
import logging
- 로그를 출력하거나 파일로 남기는 작업 수행
from flask import current_app
from werkzeug.utils import secure_filename
from PIL import Image, ImageMath
from flask import current_app
- flask
- 웹 개발, 배포를 할 수 있는 도구들을 최소한의 크기로 줄인 웹 프레임워크
- current_app
- 현재 요청을 처리하는 애플리케이션 인스턴스에 접근할 때 사용
프레임 워크란?
반복적인 작업을 줄이기 위한 뼈대와 도구의 집합
from 모듈명 import 객체명
- 특정 모듈에서 함수,변수 등 원하는 객체만 선택적으로 가져와 사용
from werkzeug.utils import secure_filename
- werkzeug
- HTTP 요청과 응답을 처리하는데 사용하는 핵심 라이브러리
- secure_filename
- 업로드된 파일을 안전하게 보호하기 위한 필수 함수
- ( 위험한 문자 제거, 디렉토리 탐색 방지, 안전한 파일 이름으로 변경 )
프레임워크 vs 라이브러리
프레임워크 : 사용자의 코드를 실행 (틀)
라이브러리 : 사용자의 코드 안에서 실행되는 것 (요소)
def allowed_file(filename):
if not filename or '.' not in filename:
return False
ext = filename.rsplit('.', 1)[1].lower()
return ext in ALLOWED_EXTENSIONS
ext = filename.rsplit('.', 1)[1].lower()
- rsplit(구분자,분할할 최대 횟수)
- 오른쪽에서부터 특정 구분자를 기준으로 분할하여 리스트로 반환
- lower()
- 모든 문자들을 소문자로 바꿈
def validate_file(file_storage):
if not file_storage or not file_storage.filename:
return False, "파일이 선택되지 않았습니다."
filename = secure_filename(file_storage.filename)
if not filename:
return False, "유효하지 않은 파일명입니다."
if '.' in filename:
ext = filename.rsplit('.', 1)[1].lower()
if ext not in ALLOWED_EXTENSIONS:
return False, f"허용되지 않는 확장자입니다. ({', '.join(ALLOWED_EXTENSIONS)}만 허용)"
secure_filename(file_storage.filename)
- 파일 보안을 위한 함수
{', '.join(ALLOWED_EXTENSIONS)}
- ‘구분자’.join(리스트)
- 리스트의 요소를 연결할 때 그 사이에 구분자를 삽입
- {} (중괄호)
- 변수를 나타낼 때, 집합이나 딕셔너리 자료형을 정의할 때 사용
try:
file_storage.seek(0)
file_data = file_storage.read()
file_size = len(file_data)
file_storage.seek(0)
if file_size > MAX_FILE_SIZE:
return False, "파일 크기가 너무 큽니다. (최대 10MB)"
if file_size == 0:
return False, "빈 파일은 업로드할 수 없습니다."
except Exception as e:
return False, f"파일 검증 중 오류가 발생했습니다: {str(e)}"
return True, "유효한 파일입니다."
file_storage.seek(0)
- seek(0)
- 파일을 여러번 읽기 위해 커서를 맨 앞으로 이동시킴
seek 함수란?
except Exception as e:
- 미리 정의된 예외를 제외한 에러 처리
- except 예외 as 변수
- 발생한 예외의 에러 메시지를 받아올 수 있음
- Exception
- 모든 예외의 부모 클래스
def get_user_upload_folder(user_uuid):
"""사용자별 업로드 폴더 경로 반환"""
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
user_folder = os.path.join(base_dir, 'static', 'uploads', 'board', user_uuid)
os.makedirs(user_folder, exist_ok=True)
return user_folder
os.path
- 파일 경로를 다루는 함수들 제공
os.path.dirname(path)
- 주어진 경로(path)에서 파일명이나 마지막 디렉토리를 제외한 상위 디렉토리 경로만 반환
os.path.abspath(path)
- 상대 경로를 절대 경로로 변경
- (전체 경로를 알 수 있음)
__file__
- 현재 실행 중인 파일의 절대 혹은 상대 경로를 담고 있는 변수
os.path.join
- 운영체제에 맞는 경로 구분자(ex. 윈도우 ‘\’)를 사용하여 여러 문자열을 하나의 경로로 연결
os.makedirs(user_folder, exist_ok=True)
- os.makedirs
- 여러 파일을 동시에 만들 수 있음
- ex) os.mkdir(”경로/폴더1/폴더2/폴더3”)
- exist_ok
- 입력한 폴더들이 다 존재하는 경우에 오류를 발생하는가
- True : 입력한 모든 경로의 모든 폴더가 존재더라도 오류
- False : 입력한 모든 경로의 모든 폴더가 존재하는 경우 FileExistError 발생
def save_file_for_user(file_storage, user_uuid):
is_valid, message = validate_file(file_storage)
filename = file_storage.filename
if not is_valid:
raise ValueError(message)
is_valid, message = validate_file(file_storage)
- 전에 미리 정의한 validate_file함수는 boolean과 에러 메시지를 반환
- (ex. (True, “유효한 파일입니다.”)라는 튜플)
- is_vaild에는 boolean, message에는 에러 메시지가 들어감
if not is_valid:
- if not
- 주어진 조건이 거짓일 때 (True가 아닐 때) 특정 코드를 실행
raise ValueError(message)
- raise Exception(”예외 메시지”)
- Exception : 발생시킬 예외의 종류 ( 일반적으로 오류 원인을 나타내는 문자열 메시지 )
if '.' in filename:
ext = filename.rsplit('.', 1)[1].lower()
filename = f"{uuid.uuid4().hex}.{ext}"
user_folder = get_user_upload_folder(user_uuid)
file_path = os.path.join(user_folder, filename)
try:
file_storage.save(file_path)
if not os.path.exists(file_path) or os.path.getsize(file_path) == 0:
raise ValueError("파일이 올바르게 저장되지 않았습니다.")
except Exception as e:
if os.path.exists(file_path):
os.remove(file_path)
raise ValueError(f"파일 저장 실패: {str(e)}")
return filename
def delete_user_file(filename, user_uuid):
if not filename:
return
try:
user_folder = get_user_upload_folder(user_uuid)
file_path = os.path.join(user_folder, filename)
if os.path.exists(file_path):
os.remove(file_path)
except Exception:
pass
except Exception: pass
- 예외가 발생하였을 때 처리하지 않고 넘어감
def save_pil_image_for_user(pil_image, user_uuid, original_filename=None):
user_folder = get_user_upload_folder(user_uuid)
ext = 'png'
if pil_image.mode == 'I':
ext = 'png'
elif ext in ['jpg', 'jpeg'] and pil_image.mode != 'RGB':
pil_image = pil_image.convert('RGB')
unique_filename = f"{uuid.uuid4().hex}.{ext}"
file_path = os.path.join(user_folder, unique_filename)
try:
pil_image.save(file_path)
except Exception as e:
raise ValueError(f"PIL Image 저장 실패: {str(e)}")
return unique_filename
if pil_image.mode == ‘I’;
- Pillow 라이브러리에서 이미지의 모드가 32비트 픽셀 형식인지 확인
image.mode
- Pillow 라이브러리에서 이미지의 색상 모드를 나타내는 문자열 (이미지의 픽셀 유형 및 깊이)
pil_image=pil_image.convert(’RGB’)
- 이미지 모드를 RGB로 변환
pil_image.save(file_path)
- 이미지를 파일에 저장
def get_user_file_url(filename, user_uuid):
if not filename:
return None
return f"/board/uploads/{user_uuid}/{filename}"
def get_processed_images(filenames, user_uuid):
upload_folder = get_user_upload_folder(user_uuid)
images = []
for fname in filenames:
file_path = os.path.join(upload_folder, fname)
try:
img = Image.open(file_path).convert('RGB')
images.append(img)
except IOError as e:
logging.error(f"Error opening image file {file_path}: {e}")
continue
if not images:
return []
return images
for fname in filenames:
- for 변수 in 시퀀스(반복대상):
images.append(img)
- images 리스트에 img를 추가
'CTF > 2025 CCE Photo Editing' 카테고리의 다른 글
| [CCE] image_processor.py (0) | 2025.09.11 |
|---|