장고에서 모델을 생성하고 그의 필드에 ImageField를 선언했을 때 , Android Studio와 연동되어
웹서버( 장고) ↔ 클라이언트(안드로이드 스튜디오)
웹서버( 장고) 측으로 클라이언트(안드로이드 스튜디오) 가 post요청을 보낼 시, ImageField로 선언되면 자동으로 걸러주는 필터링 기능이 있어보였다. (제대로 된 url주소가 아니면 post못하게 하는 것 같음)
웹서버( 장고) 측에서 클라이언트(안드로이드 스튜디오) 으로 GET을 보낼 때는 bitmap으로 바로 (url형태로 온 json값을 Bitmap으로 출력) 보여줄 수 있었으나,
POST로 보낼 때가 문제였다.
아이디어를 구현하기 위해 코드를 보면
from rest_framework import serializers
from .models import calendar, user, diary
class Base64ImageField(serializers.ImageField):
def to_internal_value(self, data):
from django.core.files.base import ContentFile
import base64
import six
import uuid
if isinstance(data, six.string_types):
# Check if the base64 string is in the "data:" format
if 'data:' in data and ';base64,' in data:
# Break out the header from the base64 content
header, data = data.split(';base64,')
try:
decoded_file = base64.b64decode(data)
except TypeError:
self.fail('invalid_image')
file_name = str(uuid.uuid4())[:12]
file_extension = self.get_file_extension(file_name, decoded_file)
complete_file_name = "%s.%s" % (file_name, file_extension, )
data = ContentFile(decoded_file, name=complete_file_name)
return super(Base64ImageField, self).to_internal_value(data)
def get_file_extension(self, file_name, decoded_file):
import imghdr
extension = imghdr.what(file_name, decoded_file)
extension = "jpg" if extension == "jpeg" else extension
return extension
class DiarySerializer(serializers.ModelSerializer):
diary_img = Base64ImageField(
max_length=None, use_url=True, required=False
)
class Meta:
model = diary
fields = ('diary_id', 'diary_title', 'diary_date', 'diary_weather', 'diary_content', 'diary_todayme', 'diary_tomorrowme', 'diary_img', )
빼먹지 않아야하는 부분은, models.py에서 diary_img 필드를 ImageField(null = True, blank = True,) 로 선언했고, serializers.py에서는 required=False 값을 줘야 원래 의도대로 이미지가 입력되지 않아도 (null)이어도 status code 400 Bad request 'No File submitted' 등의 오류를 만나지 않는다.
class diary(models.Model):
diary_id = models.AutoField(primary_key = True)
diary_date = models.DateTimeField(auto_now_add = True)
diary_weather = models.IntegerField()
diary_title = models.TextField(max_length = None,)
diary_content = models.TextField(max_length = None)
diary_todayme = models.TextField(max_length = None, null = True, blank = True)
diary_tomorrowme = models.TextField(max_length = None, null = True, blank = True)
diary_img = models.ImageField(null = True, blank = True,)
# user = models.ForeignKey(user, on_delete = models.CASCADE, null = True)
def __str__(self):
return self.diary_title
이제 요청 측인 안드로이드 스튜디오에서
갤러리를 통한 사진 (data)를 가져오고, intent가 들어오면 onActivityResult()가 호출되도록 설계한다.
이 전에 갤러리 모양의 버튼 xml을 만들고 setOnClickListener를 이용해 메소드를 호출하게 만든다.
//사진 추가 버튼 클릭 시
galleyButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
imageUpload(view);
}
});
//사진 등록 메소드
//이미지 업로드
public void imageUpload(View view) {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType(MediaStore.Images.Media.CONTENT_TYPE);
startActivityForResult(intent, 101);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 101) {
if (resultCode == RESULT_OK) {
try {
//데이터 타입 data.getData() : uri
InputStream in = getContext().getContentResolver().openInputStream(data.getData());
Bitmap img = BitmapFactory.decodeStream(in);
in.close();
write_picked_imgview.setVisibility(View.VISIBLE);
write_picked_imgview.setImageBitmap(img);
//Bitmap에서 byteArray
byterray = bitmapToByteArray(img);
//byteArray를 base64로 인코딩해서 diary_img에 넣기
diary_img = Base64.encodeToString(byterray, Base64.NO_WRAP);
} catch (Exception e) {
}
} else if (resultCode == RESULT_CANCELED) {
Toast.makeText(getContext(), "사진 선택 취소 ", Toast.LENGTH_LONG).show();
}
}
}
// Bitmap을 Byte로 변환
public byte[] bitmapToByteArray(Bitmap bitmap) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte[] byteArray = stream.toByteArray();
return byteArray;
}
코드를 보면 imageUpload메소드로 업로드된 이미지를 intent에 저장하고, startActivityForResult()를 호출,
onActivityResult메소드에서는 url타입을 되어있는 Bitmap 데이터 img를 밑에 선언한 메소드인 bitmapToByteArray를 이용해 byteArray타입의 이진파일 byterray 로 바꾸고, Base64.encodeToString를 이용해 이진파일을 모델의 필드 변수에 저장해 post를 보내면 된다.
위에서 serializers.py에서 required=False로 했기 때문에, diary_img는 null이 가능하고, 생성자를 통해 Call객체로 POST요청이 정상적으로 가게된다.