[Android] Retrofit 이미지 업로드

2 분 소요


👩 이번 시간에는 retrofit에 이미지 파일을 업로드 하는 방식에 대해 알아보고자 한다.

바로 그 방식에 대해 알아보기 전! 그 속에서 다뤄지는 multipart에 대해 정리하고 넘어가고자 한다!!


👩 HTTP

HTTP는 클라이언트와 서버 사이에 이루어지는 요청/응답(request/response) 프로토콜이다.

🙄 이미지 파일을 업로드 하는 경우?

이미지 파일(png, jpg) 자체가 서버로 넘어가는 것이 아니다!! 이미지 파일 또한 문자로 구성되기 때문에 서버로 업로드 될 때 HTTP request body에 문자 형태로 전송되게 된다.

😠 그래서! multipart가 도대체 뭔데!

🙋‍♀️ HTTP 구조

데이터를 전송할 때 HTTP Message Body에 들어가는 데이터 타입HTTP Header에 명시할 수 있다. 이 때 명시를 위해 사용되는 필드가 Content-type이다. 여기 명시되는 데이터 타입 중 하나가 바로 우리가 사용할 multipart이다.


🙋‍♀️ Multipart/for-data

👩 서버에 파일을 업로드 할 때, 웹브라우저의 경우 form을 통해 파일을 등록한다. form을 구성하는 태그는 name, action, method, enctype 등이 존재하지만 오늘 우리가 알아야 할 부분은 enctype 부분이다!!

entype: 폼 데이터(form data)가 서버로 제출될 때 데이터가 인코딩되는 방법을 명시

  • application/x-www-form-urlencoded(default)
    • 모든 문자들은 서버로 보내기 전에 인코딩된다.
  • text/plain
    • 공백 문자(space)는 “+” 기호로 변환되지만, 나머지 문자는 모두 인코딩되지 않는다.
  • ** 🔔multipart/form-data🔔**
    • 모든 문자를 인코딩하지 않는다.
    • 파일이나 이미지를 서버로 전송할 경우 주로 사용된다.


🙋‍♀️ Multipart 등장

HTTP Request Body에 들어가는 데이터는 한 종류 타입이 대부분이다. 따라서 Header에 테이터 타입을 명시하는 Contet-type에도 하나의 타입만을 명시할 수 있다.

🙄 예를 들어 text일 경우 text/plain, jpg면 image/jpeg!


그렇다면 만약 사진과 사진의 제목을 함께 서버에 보내는 상황을 생각해보자! 이때는 Content-type을 text/plain으로 해야 할까, image/jpeg로 해야 할까?

🙄 이러한 상황을 위해서 등장한 것이 Multipart이다! 하나의 Body에 대해 여러 종류의 데이터를 구분할 수 있도록 말이다!

  • Multipart 요청을 보낼 때 HTTP 메소드는 POST를 사용한다.
  • Content-Typemultipart/form-data를 사용한다.


👩 그렇다면 본격적으로 이미지를 서버에 업로드하는 과정을 알아보자!!


👩 Retrofit Multipart 통신

🙋‍♀️ PostMan으로 이미지 업로드

  • 우선 서버에 데이터를 전송하기 위해 POST를 사용한다.
  • 데이터가 들어가는 Body를 form-data type로 선택한다.
  • text / file 중 file을 선택한 후, value에 이미지를 업로드 한다.


🙋‍♀️ 코드 상 이미지 업로드 진행 (핵심)

☝️ POST 요청을 구성

    @POST("/image")
    @Multipart
    fun postImage(
        @Part file: MultipartBody.Part?): Call<ImageData>

🙄 원래는 @Body 어노테이션을 사용해 데이터를 한 방에 넘겨주었다면 Multipart에서는 각 요소마다 @Part 어노테이션을 사용해 따로 넣어주어야 한다.


✌️ API 사용하기

  • 업로드할 이미지 path를 통해 File 객체를 만든다.
      val file = File(path)
    
  • File 객체를 RequestBody로 변환한다.
      val requestBody = RequestBody.create("multipart/form-data".toMediaTypeOrNull(), file)
    
    • 여기서 잠깐!! create(MediaType?, File)의 deprecated를 해결하기 위해서는 다음 코드를 사용하면 된다!
      val requestBody = file.asRequestBody("multipart/form-data".toMediaTypeOrNull())
      
  • POST 요청시 필요한 Multipart.Part로 변환한다.
      val multipartBody = MultipartBody.Part.createFormData("file", file.name, requstBody)
    
    • createFormData()의 첫 인자는 위 PostMan에서의 key값을 적어준다!
  • API 사용하기
      ImgApiInterface.create().postImage(multipartBody).enqueue(
      object : retrofit2.Callback<ImageData>{
          override fun onResponse(call: Call<ImageData>, response: Response<ImageData>) {
              ...
              }
          }
    
          override fun onFailure(call: Call<ImageData>, t: Throwable) {
              Log.d("postImage", "error: $t")
          }
       }
       )
    


🙇‍♀️ 부족한 부분이 있다면 말씀해주세요! 감사합니다!


📃참고

댓글남기기