안드로이드/안드로이드 꿀팁

[ANDROID] Retrofit2 + String 으로 이미지 upload 하기

욧닭 2021. 9. 1. 17:35
반응형

개요

안드로이드를 개발하면서 이미지를 서버로 전송하는 여러가지 방법 중 많이 사용하는 두가지가 있습니다!!

  • base64
  • multiPartFile

 

base 64 는 이미지를 바이너리 화 한뒤 서버로 저장하는 대표적인 방식 중 하나입니다. json으로 만들어 body 에 담에 api로 보내기만 하면 upload 로직을 짤 수 있으니까요!

그러나!!! base64 는 이미지를 긴 String 으로 변환해서 api 로 보내는 것이기 때문에 용량이 크거나 사진이 여러개가 있으면 요청 파라미터의 길이가 일정 수준 넘어가버리기 때문에 서버 에러를 발생 시킬수도 있습니다

그래서 mutliPart 형식으로 데이터를 주고받는 코드를 간단히 설명 하겠습니담!

CODE

ModuleRetrofitConn.kt

class ModuleRetrofitConn(baseUrl: String) {
    private val goon : Gson = GsonBuilder().setLenient().create()
    private val retrofit : Retrofit
    private val retrofitService : RetrofitService
    init {
        this.retrofit = Retrofit.Builder()
                   .baseUrl(baseUrl)
                   .addConverterFactory(GsonConverterFactory.create(goon))
                   .build()
        this.retrofitService = this.retrofit.create(RetrofitService::class.java)
    }


    fun getService() : RetrofitService{
        return retrofitService
    }

}

기본적인 Retrofit2 설정입니다
Retrofit2Bulider 를 사용해 외부 api 통신을 위한 옵션들을 설정합니다.

자 다음으로 설정해야 할 것은 Service 로직입니다 Serviceinterface 로 구성되어 있습니다

RetrofitService.kt

interface RetrofitService {

    @Headers("accept: application/json",
        "content-type: application/json")

    @Multipart
    @POST("/v2/barcode/barcodeInsert")
    fun barcodeInsert(
            @Part reqList: List<MultipartBody.Part>,
            @Query("typeList") typeList: List<String>,
            @QueryMap model: HashMap<String,JsonObject>
    ):retrofit2.Call<ModelResInsert>
}

@Headers 는 요청을 보낼때 일괄정으로 보내는 값입니다. 모든 요청에 accept: application/jsoncontent-type: application/json 를 해더 값으로 보내겠다는 뜻입니다

@Multipart는 Part 데이터를 보낼때 주로 사용됩니다. 이 어노테이션은 무조건 인자값으로 @Part 가 선언되어야 합니다

마지막으로 실 사용 코드 입니다

TestFrag.kt

fun postData(){

        val regionService = ModuleAsisRetrofitConn("https://asisapi.gochigo.kr").getService()
        val member_id = pref.getString(ModuleAsisPreference.KEY_LOGIN,"null")
        val barcode_text : String = barcodeEditText.text.toString()


        val map = mapOf(
            "barcode_num" to barcode_text,
            "barcode_type" to boardActivity.getBarcodeType(),
            "device_group" to boardActivity.getDeviceGtoup(),
            "member_id" to member_id,
            "photo_cate" to boardActivity.getPhotoCate(),
            "return_id" to boardActivity.getReturnID()) as HashMap<String, JsonObject>

        val typeList = getList(modelList,"type")
        val multiList: MutableList<MultipartBody.Part> = mutableListOf()

        for(i in 0 until getList(modelList,"img").size){
            val img = getList(modelList,"img")[i]
            val photoName = "photo_" + img.substring(img.lastIndexOf("/")+1)
            multiList.add(moduleUtil.makeMulitpartImageData(File(img), "imgList", photoName))
        }

        regionService.barcodeInsert(multiList,typeList,map).enqueue(object : Callback<ModelResInsert> {
            override fun onFailure(call: Call<ModelResInsert>, t: Throwable) {
                moduleAsisPop.showDialog("시스템 오류입니다.",0)
            }
            override fun onResponse(call: Call<ModelResInsert>, response: Response<ModelResInsert>) {
                if(response.isSuccessful){
                    Log.v("==========> body",response.body().toString)
                }
            }
        })
}

많이 길죠.... ㅎㅎ 단락식 끊어서 설명 하겠습니당

val regionService = ModuleAsisRetrofitConn("https://xxxx.xxxxx.com").getService()

api 통신을 할 곳의 url을 설정해 줍니다

val map = mapOf(
    "barcode_num" to barcode_text,
    "barcode_type" to boardActivity.getBarcodeType(),
    "device_group" to boardActivity.getDeviceGtoup(),
    "member_id" to member_id,
    "photo_cate" to boardActivity.getPhotoCate(),
    "return_id" to boardActivity.getReturnID()) as HashMap<String, JsonObject>

val typeList = getList(modelList,"type")
val multiList: MutableList<MultipartBody.Part> = mutableListOf()

for(i in 0 until getList(modelList,"img").size){
    val img = getList(modelList,"img")[i]
    val photoName = "photo_" + img.substring(img.lastIndexOf("/")+1)
    multiList.add(moduleUtil.makeMulitpartImageData(File(img), "imgList", photoName))
}

api 서버로 보낼 값들을 정의합니다
Service 로직을 보면 알겠지만 MutipartBody.Part타입과 HashMap<String,Object>타입 List<String>타입을 인자 값으로 가지게 됩니다

마지막으로 응답 부분입니다

regionService.barcodeInsert(multiList,typeList,map).enqueue(object : Callback<ModelResInsert> {
    override fun onFailure(call: Call<ModelResInsert>, t: Throwable) {
        moduleAsisPop.showDialog("시스템 오류입니다.",0)
    }
    override fun onResponse(call: Call<ModelResInsert>, response: Response<ModelResInsert>) {
        if(response.isSuccessful){
        Log.v("==========> body",response.body().toString)
        }
    }
})

응답은 onFailureonResponse를 오버라이딩 해줍니다

이렇게 되면 프론트 로직은 끝이 나게 됩니다.

이제 서버에서 받기만하면 끝입니다!

서버에서는 @RequestParam 으로 한땀한땀 요청 값들을 받아도 되지만 만들어 놓은 Model(VO) 가 있다면 @ModelAttribute를 사용해서 값을 받도록 하겠습니다

TestController.java

@RequestMapping("/barcodeInsert")
@ResponseBody
public ModelMap barcodeInsert(@ModelAttribute ModelBarcodeV2 model) throws IOException{
    ModelMap modelMap = new ModelMap();
    modelMap = barcodeService.barcodeImgCtrl(model);
    return modelMap;
}

간단합니다

Service에서 보내는 key값과 ModelBarcodeV2 의 필드 변수 이름이 일치가 된다면 자동으로 그 필드 변수에 값을 넣어줍니다.

이게 마음 것 사용하시면 됩니다 ㅎㅎ

반응형