본문 바로가기
Android

안드로이드 코틀린(Kotlin) 멀티파트(Multipart)를 이용하여 웹으로 사진 전송

by 일용직 코딩노동자 2022. 1. 17.
728x90
반응형

우선 라이브러리 먼저 추가해줍니다.

 

implementation 'com.squareup.retrofit2:converter-scalars:2.5.0'
implementation 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'com.squareup.retrofit2:converter-gson:2.1.0'

 

그리고 레트로핏 기본 셋팅부터 하도록 하겠습니다.

 

import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.converter.scalars.ScalarsConverterFactory

object RetrofitSetting {
    val API_BASE_URL = "http://192.168.0.253:8080/"
    val httpClient = OkHttpClient.Builder()

    val baseBuilder = Retrofit.Builder()
        .baseUrl(API_BASE_URL)
        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
        .addConverterFactory(GsonConverterFactory.create())

    fun <S> createBaseService(serviceClass: Class<S>?): S {
        val retrofit = baseBuilder.client(httpClient.build()).build()
        return retrofit.create(serviceClass)
    }
}

여기서 

 

interface RetrofitPath {
    @Multipart
    @POST("서버경로")
    fun profileSend(
        @Part imageFile : MultipartBody.Part
    ): Call<String>
}

이렇게 인터페이스를 하나 만드셔서 패스를 설정해주세요.

 

만약 여기서 값을 더 추가하셔서 이미지를 전송 하고 싶으시다면

 

@Multipart
@POST("서버경로")
fun profileSend(
    @Part("userId") userId: String,
    @Part imageFile : MultipartBody.Part
): Call<String>

이런식으로 하나씩 추가하시면 됩니다!

 

이제 기본 셋팅은 끝났습니다.

본격적으로 사진을 클릭했을때 일어나는 이벤트 리스너 안에 작성할 코드를 작성해볼게요.

 

우선 함수하나 만들어줄게요.

 

fun getProFileImage(){
        Log.d(TAG,"사진변경 호출")
        val chooserIntent = Intent(Intent.ACTION_CHOOSER)
        val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
        intent.type = "image/*"
        chooserIntent.putExtra(Intent.EXTRA_INTENT, intent)
        chooserIntent.putExtra(Intent.EXTRA_TITLE,"사용할 앱을 선택해주세요.")
        launcher.launch(chooserIntent)
    }

이렇게 해주시면

launcher

이부분에서 에러가 날거에요.

잠시만 기다려주세요 다른거먼저 만들구요!

 

함수 하나 더 만들어줄게요!!

 

// 절대경로 변환
    fun absolutelyPath(path: Uri?, context : Context): String {
        var proj: Array<String> = arrayOf(MediaStore.Images.Media.DATA)
        var c: Cursor? = context.contentResolver.query(path!!, proj, null, null, null)
        var index = c?.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
        c?.moveToFirst()

        var result = c?.getString(index!!)

        return result!!
    }

해당 함수는 나중에 intent로 가져온 이미지패스를 절대경로로 변경해주는 함수입니다.

 

이제 아까 에러났던 부분 수정해볼게요.

 

var launcher = registerForActivityResult(StartActivityForResult()) { result ->
        if (result.resultCode == RESULT_OK) {
            val imagePath = result.data!!.data

            val file = File(absolutelyPath(imagePath, this))
            val requestFile = RequestBody.create(MediaType.parse("image/*"), file)
            val body = MultipartBody.Part.createFormData("proFile", file.name, requestFile)

            Log.d(TAG,file.name)

            sendImage(body)
        }
    }

자 이렇게 셋팅해주시면

아까

getProFileImage

이녀석을 호출했을때 클릭하면 

launcher

이녀석이 호출됩니다.

 

그럼 안에 소스를 보시면

 

우선 imagePath에 갤러리나 파일앱에서 가져온 데이터를 저장합니다.

 

그리고 아까 셋팅해둔 절대경로로 변환하는 함수를 사용하여 file에 저장해줍니다.

 

이후에

RequestBody.create

해당 구문을 통해 Request형식으로 바꿔줍니다.

 

그 이후에

 

MultipartBody.Part.createFormData

이 기능을 이용하여 멀티파트를 이용 할 수 있는 데이터로 만들어둡니다.

 

그리고 sendImage 하면 되는데요.

 

//웹서버로 이미지전송
    fun sendImage(userCd : String ,image : MultipartBody.Part) {
        val service = RetrofitSetting.createBaseService(RetrofitPath::class.java) //레트로핏 통신 설정
        val call = service.profileSend(userCd, image)!! //통신 API 패스 설정

        call.enqueue(object : Callback<String>{
            override fun onResponse(call: Call<String>, response: Response<String>) {
                if (response?.isSuccessful) {
                    Log.d("로그 ",""+response?.body().toString())
                    Toast.makeText(applicationContext,"통신성공",Toast.LENGTH_SHORT).show()
                }
                else {
                    Toast.makeText(applicationContext,"통신실패",Toast.LENGTH_SHORT).show()
                }
            }

            override fun onFailure(call: Call<String>, t: Throwable) {
                Log.d("로그 ",t.message.toString())
            }
        })
    }

자 이렇게 해주시면 끝나는데요!

 

여기서 통신 하실때 onFailure에서 로그가 

 

Expected a string but was BEGIN_OBJECT at line 1 column 2 path $

 

이런식으로 찍히는분들은 아까~ 위에

RetrofitSetting

이 오브젝트 클래스 설정하실때

 

.addConverterFactory(ScalarsConverterFactory.create())

이거 하나 추가하셔서

 

object RetrofitSetting {
    val API_BASE_URL = "http://192.168.0.253:8080/"
    val httpClient = OkHttpClient.Builder()

    val baseBuilder = Retrofit.Builder()
        .baseUrl(API_BASE_URL)
        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
        .addConverterFactory(ScalarsConverterFactory.create())
        .addConverterFactory(GsonConverterFactory.create())

    fun <S> createBaseService(serviceClass: Class<S>?): S {
        val retrofit = baseBuilder.client(httpClient.build()).build()
        return retrofit.create(serviceClass)
    }
}

이렇게 해주시면 됩니다!

 

ㄱ ㅏ   ㄱ  푸시

ㅗ o    ㅗ  푸시 :D

728x90
반응형

댓글