반응형
지금 진행중인 토이 프로젝트에서 게시글 등록 시 게시글 내용 + 이미지 파일 업로드를 받는 로직을 구현하는데. 이에 대해서 간단히 정리하도록 하겠다.
일반적으로 Multipart 요청에 경우 Spring Web MVC에선 다음과 같은 방식을 제안한다.
@Controller
public class FileUploadController {
@PostMapping("/form")
public String handleFormUpload(@RequestParam("name") String name,
@RequestParam("file") MultipartFile file) {
if (!file.isEmpty()) {
byte[] bytes = file.getBytes();
// store the bytes somewhere
return "redirect:uploadSuccess";
}
return "redirect:uploadFailure";
}
}
- 이미지 파일을 여러 개 받는 경우에
List<MultipartFile>
타입을 사용면 같은 파라미터 이름을 가진 이미지 파일들을List<MultipartFile>
에 바인딩해준다. Map<String, MultipartFile> or MultiValueMap<String, MultipartFile>
를 사용하면 파일 이름이 각각 다른 이미지 파일들을 바인딩할 수도 있다. 단, 이 경우에는@RequestParam
의 name속성을 정의하지 않아야함.- Servlet 3.0 이상인 경우
MultipartFile
대신에javax.servlet.http.Part
를 사용할 수 있다.
form-data를 오브젝트에 바인딩하기
class MyForm {
private String name;
private MultipartFile file;
// ...
}
@Controller
public class FileUploadController {
@PostMapping("/form")
public String handleFormUpload(MyForm form, BindingResult errors) {
if (!form.getFile().isEmpty()) {
byte[] bytes = form.getFile().getBytes();
// store the bytes somewhere
return "redirect:uploadSuccess";
}
return "redirect:uploadFailure";
}
}
- form데이터의 text 데이터들과 file 데이터를 한 번에 Object에 바인딩할 수있다.
아래와 같이 Json과 file을 같이 보내는 요청에 대해 각각 바인딩 받을 수 있다.
http 요청 예시
POST /someUrl
Content-Type: multipart/mixed
--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp
Content-Disposition: form-data; name="meta-data"
Content-Type: application/json; charset=UTF-8
Content-Transfer-Encoding: 8bit
{
"name": "value"
}
--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp
Content-Disposition: form-data; name="file-data"; filename="file.properties"
Content-Type: text/xml
Content-Transfer-Encoding: 8bit
... File Data ...
위의 요청을 핸들링하는 Controller 예시
@PostMapping("/")
public String handle(@RequestPart("meta-data") MetaData metadata,
@RequestPart("file-data") MultipartFile file) {
// ...
}
@RequestPart
를 사용하면 Json 파일로 넘어온 데이터를 바인딩할 수 있다.
@PostMapping("/")
public String handle(@Valid @RequestPart("meta-data") MetaData metadata,
BindingResult result) {
// ...
}
javax.validation.Valid
나 스프링이 제공하는@Validated
어노테이션을 붙여 사용할 수도 있음.Errors
나BindingResult
로 에러를 핸들링할 수 있다.
검증
이제 위의 내용을 검증해보도록 하자. 검증할 내용은 두 가지이다.
- form-data로 text 필드 데이터와 file 데이터를 한 번에 보내면 한 Object에 바인딩 하는 지
- Json + file 형태로 보내는 요청의 데이터를 바인딩 하는 지
form-data의 field와 file을 한 번에 바인딩 받을 MyForm 클래스
public class MyForm {
private String name;
private MultipartFile file;
...
}
이미지 파일에 대한 MetaData를 Json을 통해 바인딩 받을 MetaData 클래스
public class MetaData {
private String name;
private String someString;
private int someNumber;
...
}
검증을 위한 SampleController
@Controller
public class SampleController {
@PostMapping("/form")
@ResponseBody
public String handleFormUpload(MyForm form, BindingResult errors) {
System.out.println(form);
return "Ok";
}
@PostMapping("/form/requestpart")
@ResponseBody
public String handleJsonAndFormUpload(@RequestPart("meta-data") MetaData metadata,
@RequestPart("file-data") MultipartFile filedata) {
System.out.println(metadata);
System.out.println("[originalFileName=" + filedata.getOriginalFilename() + "]");
return "Ok";
}
}
테스트 코드 작성
@RunWith(SpringRunner.class)
@WebMvcTest(SampleController.class)
@AutoConfigureMockMvc
public class SampleControllerTest {
@Autowired
MockMvc mockMvc;
@Autowired
ObjectMapper objectMapper;
@Test
public void 파일업로드_테스트() throws Exception { // (1)
MockMultipartFile image = new MockMultipartFile("file", "imagefile.jpeg", "image/jpeg", "<<jpeg data>>".getBytes());
this.mockMvc
.perform(multipart("/form")
.file(image)
.param("name", "value")
.contentType(MediaType.MULTIPART_FORM_DATA)
.accept(MediaType.APPLICATION_JSON)
.characterEncoding("UTF-8"))
.andExpect(status().isOk());
}
@Test
public void json_and_image_파일업로드_테스트() throws Exception { // (2)
MockMultipartFile image = new MockMultipartFile("file-data", "filename-1.jpeg", "image/jpeg", "<<jpeg data>>".getBytes());
String content = objectMapper.writeValueAsString(new MetaData("name", "stringSomething", 100));
MockMultipartFile json = new MockMultipartFile("meta-data", "jsondata", "application/json", content.getBytes(StandardCharsets.UTF_8));
this.mockMvc
.perform(multipart("/form/requestpart")
.file(json)
.file(image)
.contentType("multipart/mixed")
.accept(MediaType.APPLICATION_JSON)
.characterEncoding("UTF-8"))
.andExpect(status().isOk());
}
}
- form-data로 날아온 데이터들을 한 Object로 바인딩 받는 테스트
MyForm
클래스의 필드명과 form-data 요소들의 이름을 맞춰줌
- Json + 이미지 파일로 데이터를 구성한 요청을 바인딩하는 테스트
@RequestPart
의 name 속성에 주입한대로 파일 이름을 맞춰줌
위의 테스트로 데이터들을 의도한대로 바인딩 되는 것을 볼 수 있었다. Controller에 작성한 System.out.println()에 아래와 같이 찍히는 것을 확인할 수 있었음.
MyForm [name=value, file=imagefile.jpeg]
MetaData [name=name, someString=stringSomething, someNumber=100]
[originalFileName=filename-1.jpeg]
참고
Spring Web MVC 공식문서 - https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-multipart
반응형
'Study > spring' 카테고리의 다른 글
JPA 엔티티 복합키(Composite Primary Keys) 매핑 (2) | 2021.09.13 |
---|---|
Spring - 컨트롤러 메소드 파라미터에 대한 공통 처리 로직을 한 번에! HandlerMethodArgumentResolver 기본 정리 (0) | 2021.03.27 |
Spring Cache Abstraction 정리 (0) | 2021.02.09 |
Spring REST Docs 개념 및 간단한 예제 (0) | 2021.02.02 |
QueryDSL(3) - 프로젝션, distinct, 동적 쿼리, 벌크 연산 (0) | 2021.01.26 |