처음 과정은 파일업로드가 이루어지고 요청(request)가 이루어지면, 파일업로드에 의한 데이터는 request.FILES
에 담긴다.
request.FILES에 들어오는 형식은 파일의 크기에 따라 두 가지로 나뉜다.
InMemoryUploadedFile
: 파일이 작을 경우 메모리에 저장된다. 기본적으로 Django 설정에서 FILE_UPLOAD_MAX_MEMORY_SIZE 값을 초과하지 않는 파일은 메모리에 저장된다.TemporaryUploadedFile
: 파일이 클 경우 임시 디스크 파일로 저장된다.(필자의 프로젝트는 테스트 케이스가 모두 작은 크기의 사진이었기 때문에 InMemoryUploadedFile이었다.)
수많은 양의 데이터를 처리하기 위한 데이터베이스가 필요하지 않고 파일시스템이 필요한 경우가 있을 것이다. 예로 사진파일의 경우 수 많은 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)을 넣어주어 객체를 생성한다.