말 그대로 Multi(여러) Part(부분)으로 데이터를 보내주는 것!
합동세미나에서 했던 당근 글쓰기 뷰 서버 파트에서는
제목, 가격, 내용에 추가적으로 '이미지'까지 보내야함.
근데 내가 원래 했던 서버통신의 content-type
은 application/json
인데
json으로는 이미지나 파일을 보내주기 어렵다!
-> 그래서 MultiPart/form-data
사용!!
그래서 멀티파트를 어케 쓰냐면요.
//WriteService.kt
interface WriteService {
@Multipart
@POST("/item")
fun postItem(
@Part image: List<MultipartBody.Part>,
@PartMap data: HashMap<String, RequestBody>
): Call<ResponseWrite>
}
Call<ResponseWrite>
로 해주었습니다~!(근데여기서하나..! 나는 뷰모델을 써서 내용을 다 WriteViewModel 안에 썼다는거!)
//image는 잠시 뒤로 미뤄두고 우선 string들 코드설명부터 적어볼게요
fun multipart(
title: String,
price: String,
content: String,
image: List<Bitmap>
) {
val titleRequestBody =
title.toRequestBody("text/plain".toMediaTypeOrNull())
val priceRequestBody =
price.toRequestBody("text/plain".toMediaTypeOrNull())
val contentRequestBody =
content.toRequestBody("text/plain".toMediaTypeOrNull())
val requestBodyHashMap = HashMap<String, RequestBody>()
requestBodyHashMap["title"] = titleRequestBody
requestBodyHashMap["price"] = priceRequestBody
requestBodyHashMap["contents"] = contentRequestBody
price는 원래 int라고 명세서에 써있지만.. string으로 보내도 서버 측에서는 문제가 없다고 합니다 헤헹
toRequestBody()
는 RequestBody
로 바꿔주는 역할
"text/plain".toMediaTypeOrNull())
에서 toMediaTypeOrNull()
은 그 type
으로 바꿔주는 역할이다. 여기서는 String
을 text/plain
으로 바꿔주는 거겠징
title.toRequestBody("text/plain".toMediaTypeOrNull())
는 text/plain
타입의 requestbody가 된 것이고
requestBodyHashMap["title"] = titleRequestBody
title
이라는 key에 titleResquestBody
라는 value 할당!
다른 price, contents도 마찬가지
이제 이미지를 볼까요
companion object {
class BitmapRequestBody(private val bitmap: Bitmap) : RequestBody() {
override fun contentType(): MediaType? {
return "image/png".toMediaTypeOrNull()
}
override fun writeTo(sink: BufferedSink) {
bitmap.compress(Bitmap.CompressFormat.PNG, 99, sink.outputStream()) //99프로 압축
}
}
}
BitMap
을 RequestBody
로 바꿔주는 클래스RequestBody
상속받아 오버라이딩 함override fun contentType()
이건 어떤 타입으로 바꿀거냐임.override fun writeTo
이건 이미지를 압축하는 함순데 png로 압축을 할거임. 99% 압축한다는 의미for (i in 0 until image.size) {
val imageRequestBody = BitmapRequestBody(image[i])
...
val imageRequestBody = BitmapRequestBody(image[i])
는 배열의 i번째 인덱스를 위에서 만들었던 BitmapRequestBody 함수에 넣어주는 거! 얘가 이제 requestbody가 됨.@Part image: List<MultipartBody.Part>
라고 해놨으니 멀티파트로 바꿔줘야겠지요?//위 코드에 이어서 작성
val imageMultipartBody: MultipartBody.Part =
MultipartBody.Part.createFormData(
"image",
"and" + System.currentTimeMillis()
.toString() + i.toString(), // 승현 - index 를 활용해 이미지 이름을 구분합니다.
imageRequestBody
)
imageListMultipartBody.add(imageMultipartBody)
}
System.currentTimeMillis()
추가imageRequestBody
얘를 멀티파트로 만들어준다는 말!imageListMultipartBody.add(imageMultipartBody)
= 리스트에 방금 만든 imageMultipartBody를 추가해줌
val call: Call<ResponseWrite> =
ServiceCreator.writeService.postItem(imageListMultipartBody, requestBodyHashMap)
call.enqueue(object : Callback<ResponseWrite> {
override fun onResponse(
call: Call<ResponseWrite>,
response: Response<ResponseWrite>
) {
if (response.isSuccessful) {
Log.d("NetworkTest", "success")
_isMultiPartSuccess.call()
} else {
Log.d("NetworkTest", "connected but failed")
}
}
override fun onFailure(call: Call<ResponseWrite>, t: Throwable) {
Log.e("NetworkTest", "error:$t")
}
})
ServiceCreator.writeService.postItem()
에 파라미터로 imageListMultipartBody, requestBodyHashMap
를 넣어주고멀티파트 여태까지 뷰모델에 썼으니까 호출해줘야겠죠?
WriteFragment.kt
private fun clickEvent() {
binding.tvFinish.setOnClickListener {
if (writeViewModel.writeTitle.value?.length == 0 || writeViewModel.writePrice.value?.length == 0 || writeViewModel.writeContent.value?.length == 0 || writeViewModel.selectedImageList.value?.size == 0) {
Toast.makeText(requireContext(), "채워지지 않은 부분이 있습니다", Toast.LENGTH_SHORT).show()
} else {
writeViewModel.multipart(
requireNotNull(writeViewModel.writeTitle.value),
requireNotNull(writeViewModel.writePrice.value),
requireNotNull(writeViewModel.writeContent.value),
requireNotNull(writeViewModel.selectedImageList.value).map { requireNotNull(it.first.image) }
)
}
}
binding.btnBack.setOnClickListener {
requireActivity().finish()
}
}
requireNotNull
을 사용해주는데, value가 null이 아닐 때 갖다 쓴다는 의미! null이 아니라는 걸 보장해주는것..본 내용은 한승현씨 강의를 바탕으로 작성했음을 알려드립니다!~ ㅋㅋ
한교수님 짱 ㅋㅋ