[Django] request.FILE to Form 이미지 처리 InMemoryUploadedFile

jkky98·2024년 5월 17일
0

Django

목록 보기
3/8

프로세스

처음 과정은 파일업로드가 이루어지고 요청(request)가 이루어지면, 파일업로드에 의한 데이터는 request.FILES에 담긴다.

request.FILES에 들어오는 형식은 파일의 크기에 따라 두 가지로 나뉜다.

  • InMemoryUploadedFile: 파일이 작을 경우 메모리에 저장된다. 기본적으로 Django 설정에서 FILE_UPLOAD_MAX_MEMORY_SIZE 값을 초과하지 않는 파일은 메모리에 저장된다.
  • TemporaryUploadedFile: 파일이 클 경우 임시 디스크 파일로 저장된다.

(필자의 프로젝트는 테스트 케이스가 모두 작은 크기의 사진이었기 때문에 InMemoryUploadedFile이었다.)

파일 vs 데이터

  • 파일 : 데이터의 집단 ( 이미지, 음성, 비디오 등등), 확장자 존재(jpg, py 등등)
  • 데이터 : 말 그대로 데이터(문자열 데이터, 정수형 데이터 등등)

수많은 양의 데이터를 처리하기 위한 데이터베이스가 필요하지 않고 파일시스템이 필요한 경우가 있을 것이다. 예로 사진파일의 경우 수 많은 rgb값이나 그 위치에 대한 데이터들이 특정한 형식(ex.바이너리)으로 어떠한 룰에 의해 묶여서 컴퓨터가 해석한다.

(사진 하나를 컴퓨터 창에 띄우기 위해 데이터베이스 보관함을 만들고 rgb값을 저장하고 사용하는 것은 비효율이니까)

활용

Django의 Form은 파일과 데이터를 따로 주어야 했다.

만약 dataset = {데이터, 사진파일(바이너리 데이터), 사진파일(바이너리 데이터)} 이렇게 담아서 받지 않는다. 사진파일 자체가 바이너리 데이터로 여겨지기에 "dataset에 데이터를 담아서 전송했는데 왜 Form에 도착하지 않는거지??" 로 생각해서 사진파일을 django form에서 받아들이도록 튜닝시키려고 할 필요 없이 Form에 data와 request.FILES를 전달하면 된다.

django에서는 사진파일(ex.InMemoryUploadedFile을 장고의 내장 자료구조인 MultiValueDict에 저장해서 활용한다. 반면 일반 POST 데이터의 경우 QueryDict에 담아 활용한다)

사진파일(InMemoryUploadedFile)은 MultiValueDict에 아래처럼 저장된다.(request.FILES가 파일을 가진 MultiValueDict이다.)

from django.core.files.uploadedfile import InMemoryUploadedFile
from io import BytesIO
from django.utils.datastructures import MultiValueDict

# 가상의 이미지 파일 생성
image_content_1 = BytesIO(b'Binary data of image 1')
image_content_1.seek(0)
image_1 = InMemoryUploadedFile(
    image_content_1,
    None,
    'image_1.jpg',  # 파일 이름
    'image/jpeg',   # MIME 타입
    len(image_content_1.read()),  # 파일 크기
    None
)

image_content_2 = BytesIO(b'Binary data of image 2')
image_content_2.seek(0)
image_2 = InMemoryUploadedFile(
    image_content_2,
    None,
    'image_2.jpg',  # 파일 이름
    'image/jpeg',   # MIME 타입
    len(image_content_2.read()),  # 파일 크기
    None
)

# MultiValueDict에 이미지 파일 추가
files_dict = MultiValueDict()
files_dict.appendlist('image_files', image_1)
files_dict.appendlist('image_files', image_2)

'''
<MultiValueDict: {'image_files': 
[<InMemoryUploadedFile: image_1.jpg (image/jpeg)>,
<InMemoryUploadedFile: image_2.jpg (image/jpeg)>]}>
'''

처리과정을 코드로 보자.

사용할 Model은 3개의 필드(사진1, 사진2, User객체)를 가지고 Form은 이 세 개의 필드를 활용한다고 생각하자.

# views.py의 함수의 한 부분..
# item1과 item2는 브라우저를 통해 로컬에서 업로드된 사진 파일이다.
# 업로드된 사진 파일은 위에서 설명한 두 가지 형식중 하나로 request.FILES에 담긴다.
item1 = request.FILES.get('item1', None)
item2 = request.FILES.get('item2', None)

user = request.Post.get('user')
dataset = {
            user = user
        }
        
form = CustomForm(dataset, request.FILES)
     if form.is_valid():
     	# valid 성공시 로직 처리

InMemoryUploadedFile을 Form에서 매핑시키기 위해서 InMemoryUploadedFile의 속성인 name을 사용한다.

템플릿에서 파일업로드 태그를 다음과 같이 name을 주어서 다루면 된다.

#template(html)
<input type="file" name="item1" />
<input type="file" name="item2" />

#forms.py
#form 클래스의 save 메서드
item1 = self.cleaned_data.get('item1')
item2 = self.cleaned_data.get('item2')
user = self.cleaned_data.get('user')

# 모델로 DB에 레코드 저장
my_model = MyModel(user=user, item1=item1, item2=item2)
my_model.save()

return my_model

cleaned_data는 is_valid()가 성공해야 cleaned_data에 Form에 매핑된 데이터들이 들어오므로 명심하자.

그리고 모델에 form필드들을 get한 필드들(user, item1, item2)을 넣어주어 객체를 생성한다.

정리

  1. 클라이언트(user)가 사진파일을 업로드
  2. 클라이언트가 서버(해당하는 views.py의 함수 작동)로 request 보냄
  3. django의 뷰 함수에서 받은 request.FILES는 MultiValueDict로 되어있으며, 그 안에는 파일객체가 존재(파일은 장고의 내장 자료구조인 InMemoryUploadedFile객체로 바이너리데이터, 파일의 이름, 확장자 등등이 담김)
  4. Form에 데이터와 파일을 구분해서 전달
  5. Form에서 name에 대응되게 처리
profile
자바집사의 거북이 수련법

0개의 댓글