이전 포스팅에는 파일을 동기적으로 업로드하는 방식에 대해 알아보았는데 이어서 비동기적으로 파일을 업로드 해보겠습니다.
프로젝트를 진행하면서 파일을 업로드할 때 사용자가 서버에 파일이 끝까지 저장될 때까지 계속해서 응답을 기다리게 되는 문제가 발생했습니다.
진행한 프로젝트에서는 단순히 파일을 업로드하고 끝나는게 아니라 영상에 필터를 입히고 다양한 처리를 해줘야하기 때문에 시간이 꽤 걸리는 작업이었습니다. 이렇게 되면 사용성이 떨어지기 때문에 이를 개선하고자 비동기적으로 먼저 응답을 보내는 방식으로 코드를 작성했습니다.
스프링에서 비동기 메서드를 작성하는 방법은 참고
BasicController
@RestController
@RequiredArgsConstructor
public class BasicController{
private final BasicService service;
@Value("${file.dir}")
private String fileDir;
@PostMapping("/upload")
public ResponseEntity<String> saveFile(@RequestParam String itemName, @RequestParam MultipartFile file) {
if (!file.isEmpty()) {
String fullPath = fileDir + file.getOriginalFilename()
log.info("-------BasicController 비동기 처리 메소드 실행-------");
service.processFile(file,fullPath);
log.info("-------BasicController 비동기 처리 메서드 종료-------");
}
return new ResponseEntity<>(fullPath, HttpStatus.CREATED);
}
}
BasicService
@Service
@Async
public class BasicService {
public void processFile(MultipartFile file,String fullPath) {
if (!file.isEmpty()) {
log.info("-------BasicService 비동기 처리 실행 -------");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
file.transferTo(new File(fullPath));
} catch (IOException e) {
throw new RuntimeException(e);
}
log.info("-------BasicService 비동기 처리 종료 -------");
}
}
}
java.lang.RuntimeException: java.io.IOException: java.io.FileNotFoundException: C:\Users\AppData\Local\Temp\tomcat.8080.3037933434984946\work\Tomcat\localhost\ROOT\upload_a81d4bd7_c23a_43ba_9016_43e59017d84a_00000001.tmp (지정된 파일을 찾을 수 없습니다)
분명 비동기적으로 잘 처리된 거 같은데 다음과 같은 에러가 발생.. 그 이유는?
BasicController
에서 BasicService
로 MulitpartFile 객체인 file
을 전달하는데 이때 응답이 먼저 비동기적으로 보내지면 file
객체는 메모리에서 deallocate되버린다. 참고 따라서 BasicService
에서 전달 받은 file
객체를 처리하려고 하면 FileNotFoundException
발생하는 것! 해결 방법은 BasicService
로 전달하기 전 메모리에 저장하기..!
BasicController
@RestController
@RequiredArgsConstructor
public class BasicController{
private final BasicService service;
@Value("${file.dir}")
private String fileDir;
@PostMapping("/upload")
public ResponseEntity<String> saveFile(@RequestParam String itemName, @RequestParam MultipartFile file) {
if (!file.isEmpty()) {
String fullPath = fileDir + file.getOriginalFilename()
log.info("-------BasicController 비동기 처리 메소드 실행-------");
service.processFile(file.getBytes(),fullPath);
log.info("-------BasicController 비동기 처리 메서드 종료-------");
}
return new ResponseEntity<>(fullPath, HttpStatus.CREATED);
}
}
BasicService
public void processFile(byte[] file,String fullPath) {
...
}
만약에 동영상을 보내고 응답을 보냈는데 파일 처리중에 에러가 발생하면 클라이언트에게 어떻게 알려주지?
애초에 큰 파일을 클라이언트에서 서버로 전송하는데 시간이 오래걸린다.