달달한 스토리

728x90
반응형

출처 핀터레스트

 

최근에 새로운 앱을 만들기 위해 고군분투 중이다.

 

기존 앱에 서버에서 받는 api의 원시 데이터는 xml을 사용했는데,

 

이번 앱은 json을 사용하게 되었다.

 

다행이 인터넷에 자료가 많아 데이터를 보내는데 문제가 없었다.

 

그렇지만, 이미지를 보내는 과정에서 어려움이 생겼다.

 

서버 개발자분이 Multpart를 통해서 서버로 이미지를 보내달라고 요청하셨다.

 

멀티파트??

 

구글에 쳐본 결과

 

멀티파트란?

 

- HTTP 요청의 한 종류로서 서버에 파일이나 데이터를 보내기 위한 요청 방식이다.

- 보통은 큰 용량의 바이너리 데이터 전송에 적합하다.

 

한마디로 큰 이미지 파일 등을 서버로 넘겨줄 때 적합한 방법이다.

 

레트로핏을 이용해서 사용해보자

 

(물론 레트로핏 종속성과 실행법은 모두 알고 있다는 전제로 설명하겠다.)

 

데이터를 받는 DTO클래스 역시 생략하겠습니다.

 

1. 이미지 파일을 하나만 넘겨줄 때

 

Api interface를 보자

 

 @Multipart
    @POST("serverlink")
    fun postImage(
        @Part scanImage: MultipartBody.Part
    ) : Call<DTO>

 

이미지를 보내기 위한 api 인터페이스는 다음과 같이 어노테이션을 달아준다.

 

@Multipart 어노테이션을 달아서 멀티파트를 사용할 것이라는 걸 명시해주고,

 

데이터를 받게 되는 파라미터 값도 @Part 어노테이션을 달아준다. 

 

그리고 이미지를 보내기 위해 파일은 다음 타입으로 지정한다. MultipartBody.Part

 

이제 실행 코드를 살펴보자.

 

 /*
   * 이 부분만 보면 된다.
   * */
fun postImage(uri: Uri, context: Context) {
	val file = File(uri.path!!)
    val stream = compressImg(context, uri)
    val requestBody: RequestBody = stream.toByteArray().toRequestBody("image/jpg".toMediaTypeOrNull())
    val uploadFile =
            MultipartBody.Part.createFormData(
                "서버에서 해당 이미지 키값",
                file.name + "." + (getMimeType(context, scanImg) ?: ".jpg"),
                requestBody)
}

 /*
   * 이미지 uri 를 퀄리티 20을 낮추어 압축하여 반환하는 메서드이다.
   * */
    fun compressImg(context: Context, img: Uri): ByteArrayOutputStream {
        var inputStream: InputStream? = null
        try {
            inputStream = context.contentResolver.openInputStream(img)
        } catch (e: IOException) {
            e.printStackTrace()
        }
        val bitmap = BitmapFactory.decodeStream(inputStream)
        val byteArrayOutputStream = ByteArrayOutputStream()
        bitmap.compress(
            Bitmap.CompressFormat.JPEG,
            20,
            byteArrayOutputStream
        )
        return byteArrayOutputStream
    }
    
    /*
    * 확장자를 가져오는 메서드이다.
    * */
    fun getMimeType(context: Context, uri: Uri): String? {
    
        val extension: String? = if (uri.scheme == ContentResolver.SCHEME_CONTENT) {
            val mime = MimeTypeMap.getSingleton()
            mime.getExtensionFromMimeType(context.contentResolver.getType(uri))
        } else {
            MimeTypeMap.getFileExtensionFromUrl(Uri.fromFile(File(uri.path!!)).toString())
        }
        return extension
    }

postImage 메서드만 보면 된다.

 

나머지 메서드는 내가 같이 사용하면 좋을 메서드를 부가적으로 넣은 것이다.

 

우선 postImage부터 설명하겠다.

 

1. 파라미터로 받은 uri를 통해 uri경로를 File객체에 넣어주어 새로운 파일을 생성한다.

 

2. (선택) 보통 이미지를 넣게 되면 사이즈가 클 수 있어, compressImg 메서드를 만들어서 사용하였다.

이미지를 압축하여 보내기위함이다. (그냥 쓰는 것을 추천한다.)

 

3. 이 압축한 이미지 파일을 RequestBody에 담아준다.

 

4. 파일을 담은 RequestBody를 MultipartBody.Part로 한번 더 감싸준다.

이때 createFormData의 첫 번째 인자가 서버에서 사용되는 해당 데이터 키 값과 같아야 한다는 점을 유의 하자.

 

이렇게 만들고 레트로핏을 이용해서 postImage 메서드의 인자로 넣어주면 되는 것이다.

 

 

2. 데이터를 넘겨줄 때

 

멀티파트로 데이터를 넘겨주는 일도 있다.

 

데이터들은 PartMap으로 맵에 담아 보내줘야한다.

val requestMap: HashMap<String, RequestBody> = HashMap()
        requestMap["서버에서 해당 키값"] = "넘겨줄 텍스트".toRequestBody("text/plain".toMediaTypeOrNull())
        requestMap["서버에서 해당 키값2"] = "넘겨줄 텍스트2".toRequestBody("text/plain".toMediaTypeOrNull())
        requestMap["서버에서 해당 키값3"] = (나 Int).toString().toRequestBody("text/plain".toMediaTypeOrNull())
        requestMap["서버에서 해당 키값3"] = (나 Double이나 Float).toString().toRequestBody("text/plain".toMediaTypeOrNull())

 

이런 식을 스트링 값으로 text/plain 미디어 타입 RequestBody 타입으로 만들어 줘야 한다.

 

여기서 중요한 점은 서버에 넘길 때, String Type만 가능하다는 것이다.

 

RequestBody에는 숫자형 데이터가 들어갈 수 없다고 한다.

 

고로, Int, Double, Float 등 String 제외한 타입들은 toString() 함수를 통해 String Type으로 바꾸어준다.

 

그리고 인터페이스도 다음과 같이 바꾸어준다.

 

 @Multipart
    @POST("serverlink")
    fun postImage(
        @PartMap data: HashMap<String, RequestBody>?
    ) : Call<DTO>

 

@PartMap 어노테이션을 달아주고, HasMap<String, RequestBody> 타입으로 바꾸어준다.

 

1번째 방법처럼 postImage 메서드의 인자로 넣어주면 되는 것이다.

 

3. 이미지 파일과 데이터를 같이 넘겨줄 때

 

이 방법은 굳이 설명하지 않고, 인터페이스만 보여주겠다.

 

@Multipart
    @POST("serverlink")
    fun postImage(
        @PartMap data: HashMap<String, RequestBody>?,
        @Part image: MultipartBody.Part
    ) : Call<DTO>

이런 식으로 각각 넣어주면 된다.

 

레트로핏과 함께 이용해보자.

728x90
반응형

공유하기

facebook twitter kakaoTalk kakaostory naver band
loading