@PostMapping("")
@ResponseBody
fun testOne(@RequestPart(name = "video") video: MultipartFile, title : String): String {
return "test"
}Servlet방식의 Spring을 사용할 때는 전혀 이상없이 동작했던 코드입니다. 아마 대부분의 Spring개발자분들이 이런 방식을 통해서 이미지나 동영상을 업로드받을 것이다.
심지어 환경을 리액티브로 바꾸지 않고 Webflux(안의 Reactor)만 사용할 때에는 이런 문제가 생기지 않았다. 하지만 모든 요청과 Security를 리액티브로 변경하기 위해 다음과 같이 Properties를 적용해주었다.
spring.main.web-application-type=reactive
그 후부터 문제가 발생했다. 기존의 REST API가 대부분 오류가 난다.. 특히 415나 500 코드의 오류들이었다. 그래서 왜 문제가 생기는지 10시간 정도 찾아보았습니다. Security를 리액티브로 구현하는 작업도 같이 하고 있었기 때문에 처음에 Security의 문제라고 생각한 것이 더 오래 걸리게 만들었다.
열심히 하나하나 뜯어서 실험해보다 보니 MultiPartFile이 전달되지 않는 것을 확인할 수 있었다. 그래서 왜 받아오지 못하는지 알아보았지만, WebFlux에 대한 정보가 정말 없었습니다... 그러다 찾아낸 해결책.
왜 무엇때문에 사용하지 않는지는 더 찾아봐야 겠지만, 확실한건 Request에서 MulitPartFile을 인식하지 못한다. 대신에 FilePart라는 Content를 Flux로 감싸고 있는 객체를 사용한다!!
이 점에서 봤을 때 MultiPartFile 대신 FilePart를 사용하는건 아무래도 파일을 Flux와 같은 스트림 형태로 전달하기 위함이 아닐까 생각한다.
@PostMapping("")
@ResponseBody
fun test(@RequestPart(name = "video") video: FilePart,
@RequestPart title : String,
): Mono<String> {
return Mono.just("test")
}이렇게 MultiPartFile을 FilePart로만 바꾸어주면 됩니다. 이렇게 받아온 FilePart를 어떻게 활용하면 될까요?
val path = Paths.get("지정할 URI/test.mp4") //지정한 URI에 test.mp4로 저장됩니다.
video.transferTo(path).subscribe()이 방법을 찾아내기가 쉽지 않았다. 우선 처음에 생각했던 틀린방법부터 보여드리겠습니다.
video.content()
.map(DataBuffer::asInputStream)
.doOnNext { inputStream->
inputStream
}직관적으로 봤을 때는 전혀 이상하다고 생각하지 않았는데 이렇게 동작시켜보니 1kb짜리 inputStream으로 쪼개져서 나왔다. 아무래도 거의 모든 경우 업로드한 파일은 그 자체로 의미가 있기 때문에 파일 자체의 inputStream이 필요했다.
DataBufferUtils.join(video.content())
.map(DataBuffer::asInputStream)
// DataBufferUtils를 사용해서 Flux<DataBuffer> 를 하나의 DataBuffer로 합쳐준다.DataBufferUtils.join(video.content())
.map(DataBuffer::asInputStream)
.doOnNext { inputStream ->
amazonS3.putObject(
PutObjectRequest("버킷이름", "test.mp4", inputStream, ObjectMetadata())
)
}다음과 같이 업로드하면 된다.
