그전에 포스트에서 말했다시피 대량의 json데이터들과 이미지를 어떻게 전송을 해야 할지 고민을 많이 했다. 이미지와 json데이터들을 따로 보내는 방식으로도 개발해었는데 결국 최종적으로는 base64로 이미지를 인코딩하여 스트링 형태로 만들어줘서 json객체에 담아서 한꺼번에 보내는 방식을 선택하기로 했다. 우선은 DRF을 사용하여 개발했기 때문에 모델을 먼저 정의해줬다.
class ProductImg(BaseModel):
product = models.ForeignKey(Product,on_delete=models.CASCADE, related_name='images')
image = models.ImageField(upload_to="", blank=True)
어떠한 프로덕트에 대한 이미지로 프로덕트라는 모델과 ForeignKey로 연결해주었고 밑에 이미지필드를 사용해주어서 이미지를 저장할 수 있는 필드를 만들어주었다.
class DecodeSerializer(serializers.ModelSerializer) :
def create(self, validated_data):
product = Product.objects.create(**validated_data)
#이미지 디코딩하기
bulk_list = []
num = 1
for image_string in self.context.get("images"): #base64로 인코딩된 이미지들을 불러온다.
header, data = image_string.split(';base64,')
data_format, ext = header.split('/')
try:
image_data = base64.b64decode(data)
image_root = settings.MEDIA_ROOT + '\\' + str(product.id) + "_" + str(num) + "." + ext
with open(image_root, 'wb') as f:
f.write(image_data)
bulk_list.append(ProductImg(product=product, image=f'{product.id}_{num}.{ext}'))
num += 1
except TypeError:
self.fail('invalid_image')
images = ProductImg.objects.bulk_create(bulk_list)
return product
class Meta:
model = Product
fields = '__all__'
코드를 설명 하자면 우선 이미지가 어떤식으로 인코딩되어서 오는지를 살펴보아야 한다.
이미지는 전체적으로 다음과 같이 전송되어 온다
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAnsAAALWCAYAAAA+tlWnAAAgAElEQVR4nOzdd5gcV5Xw4d+9VZ27p7sn56A0o5wtR5wJBhYbY/Lukj4wwWDSsrAYMHlZYMmYJSw ...생략...
header, data = image_string.split(';base64,')
data_format, ext = header.split('/')
try:
image_data = base64.b64decode(data)
image_root = settings.MEDIA_ROOT + '\\' + str(product.id) + "_" + str(num) + "." + ext
with open(image_root, 'wb') as f:
f.write(image_data)
bulk_list.append(ProductImg(product=product, image=f'{product.id}_{num}.{ext}'))
num += 1
except TypeError:
self.fail('invalid_image')
try-catch
으로 이미지가 제대로 된 이미지 인지 확인해준다mage_data = base64.b64decode(data)
를 통해 인코딩된 이미지가 다시 디코딩될 것이다.image_root
는 이미지가 저장되는 경로와 저장되는 이름을 정의해준다image_root
로 디코딩된 이미지를 저장시켜준다.images = ProductImg.objects.bulk_create(bulk_list)
위에 시리얼라이저에서 create을 오버라이딩해서 잘 구현해주었기 때문에 뷰에는 딱히 더해줄 코드가 없고 단순하다.
class APITest(APIView):
def post(self, request) :
data = request.data
#serializer 직렬화
serializer = DecodeSerializer(data=data, context={'request': request, 'images':data.get("images")})
# 데이터 유효성 검사
if serializer.is_valid():
# DB에 저장
serializer.save()
return Response(get_successCode(serializer.data), status=status.HTTP_201_CREATED)
return Response(get_failCode(serializer.errors), status=status.HTTP_400_BAD_REQUEST)
실제로 테스트해보면 잘 저장되는 것을 볼 수 있다. 끝~!!🥰