[Android] Retrofit2 Multipart사용하기 (Java)

th.k·2021년 1월 9일
10

Android

목록 보기
2/8

글을 쓸 당시에는 직접 구현했던 것을 토대로 작성했는데, 현재는 그럴 수가 없어 업데이트를 하지 못했습니다.
변경사항이 있다고 하니 유의 바랍니다.


Multipart

Retrofit2을 통해 서버로 파일을 전송할 때 사용하게 된다.
아마 다음과 같은 형태로 인터페이스를 정의할 것이다.

@Multipart
@POST("api주소")
Call<SomeResponse> request(파라미터들...);

파일 전송때문에 Multipart 어노테이션이 붙어서, 그냥 보낼 수 있는 타입도 Multipart에 맞게 보내야 했던 것 같다.

왜그랬는지 기억은 안나는데, file 전송할 때 자꾸 안돼서 이것저것 계속 시도했었던 것 같다.
일단 내 경우에 성공적으로 동작했던 방법을 정리한다.

file이 아닌 것

단일 항목

한개만 보내면 되는 경우, RequestBody를 사용하면 된다.

@Multipart
@POST("api주소")
Call<SomeResponse> request(@Part("key") RequestBody param);

RequestBody는 다음과 같이 만들 수 있다.

String text = "some text";
RequestBody requestBody = RequestBody.create(MediaType.parse("text/plain"), text);

위 코드는 String을 담기 때문에 MediaType을 "text/plain"으로 지정해줬다.

여러 단일 항목을 모아서 보내기

단일 항목이 많아서 파라미터를 구구절절 쓰기 싫을 때는 Map으로 모아서 보낼 수 있다.

@Multipart
@POST("api주소")
Call<SomeResponse> request(@PartMap Map<String, RequestBody> params);

다음과 같이 Map을 만들 수 있다.

RequestBody nameBody = RequestBody.create(MediaType.parse("text/plain"), "철수");
RequestBody ageBody = RequestBody.create(MediaType.parse("text/plain"), "15살");
RequestBody emailBody = RequestBody.create(MediaType.parse("text/plain"), "chulsu@email.com");

HashMap<String, RequestBody> requestMap = new HashMap<>();
requestMap.put("name", nameBody);
requestMap.put("age", ageBody);
requestMap.put("email", emailBody);

put에 들어가는 String은 서버에서 받는 key값? 필드아이디?가 되겠다.

같은 항목을 여러개 보내기

같은 항목에 대해서 배열로 보내야 할 경우 List를 사용한다.
하지만 RequestBody를 사용하지 않고, MultipartBody.Part를 사용한다.

@Multipart
@POST("api주소")
Call<SomeResponse> request(@Part List<MultipartBody.Part> params);

List<MultipartBody.Part>는 다음과 같이 만들 수 있다.

ArrayList<MultipartBody.Part> names = new ArrayList<>();
names.add(Multipart.Part.createFormData("이름", "철수");
names.add(Multipart.Part.createFormData("이름", "제임스");
names.add(Multipart.Part.createFormData("이름", "존");
// 계속 추가...

file인 것

단일 file

그냥 MultipartBody.Part 형태로 보내면 된다.

@Multipart
@POST("api주소")
Call<SomeResponse> request(@Part MultipartBody.Part file);

file을 담는 MultipartBody.Part는 다음과 같이 만든다.
나는 사진을 보냈어야 했기 때문에 예시도 사진을 보내는 경우를 썼다.

// Uri 타입의 파일경로를 가지는 RequestBody 객체 생성
RequestBody fileBody = RequestBody.create(MediaType.parse("image/jpeg"), Uri filePath);

// RequestBody로 Multipart.Part 객체 생성
MultipartBody.part filePart = Multipart.Part.createFormData("photo", "photo.jpg", fileBody);

createFormData(...)에 들어가는 파라미터는 순서대로 다음과 같은 의미를 가진다.
1. 서버에서 받는 키값 String
2. 파일 이름 String
3. 파일 경로를 가지는 RequestBody 객체

여러개의 file

같은 key값으로 여러항목을 보낼 때 처럼 List를 사용한다.

@Multipart
@POST("api주소")
Call<SomeResponse> request(@Part List<MultipartBody.Part> files);

file을 담는 List<MultipartBody.Part>는 다음과 같이 만들 수 있다.

// 여러 file들을 담아줄 ArrayList
ArrayList<MultipartBody.Part> files = new ArrayList<>();

// 파일 경로들을 가지고있는 `ArrayList<Uri> filePathList`가 있다고 칩시다...
for (int i = 0; i < filePathList.size(); ++i) {
    // Uri 타입의 파일경로를 가지는 RequestBody 객체 생성
    RequestBody fileBody = RequestBody.create(MediaType.parse("image/jpeg"), filePathList.get(i));
    
    // 사진 파일 이름
    String fileName = "photo" + i + ".jpg";
    // RequestBody로 Multipart.Part 객체 생성
    MultipartBody.part filePart = Multipart.Part.createFormData("photo", fileName, fileBody);
    
    // 추가
    files.add(filePart);
}

단일 file처럼 똑같이 RequestBody 만들고, 그걸로 MultipartBody.Part 만든 다음,
ArrayList<MultipartBody.Part>에 넣어주기만 하면 된다.

이것때문에 아주 머리가 아팠다.
자꾸 안되는데, 사람마다 방법이 아예 다른것도 아니고 찔끔찔끔 다르고, file이 아닌걸 보내는 방법과... file인데 한개만 보내는 방법과... 여러개를 보내는 방법 등이 다 정리되어 있는 글도 없어서 더 복잡했다.

profile
고생끝에롹이온다

4개의 댓글

comment-user-thumbnail
2021년 3월 3일

포스팅이 많이 도움이 되었습니다! 감사합니다 :)

1개의 답글
comment-user-thumbnail
2023년 2월 11일

23년 좀 바뀐점은 있지만 제 생명의 은인이세요

1개의 답글