// application.properties 에 app.upload.dir을 정의하고, 없는 경우에 default 값으로 user.home (System에 종속적인) @Value("${app.upload.dir:${user.home}}") private String uploadDir;
publicvoidfileUpload(MultipartFile multipartFile){ // File.seperator 는 OS종속적이다. // Spring에서 제공하는 cleanPath()를 통해서 ../ 내부 점들에 대해서 사용을 억제한다 Path copyOfLocation = Paths.get(uploadDir + File.separator + StringUtils.cleanPath(multipartFile.getOriginalFilename())); try { // inputStream을 가져와서 // copyOfLocation (저장위치)로 파일을 쓴다. // copy의 옵션은 기존에 존재하면 REPLACE(대체한다), 오버라이딩 한다 Files.copy(multipartFile.getInputStream(), copyOfLocation, StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { e.printStackTrace(); thrownew FileStorageException("Could not store file : " + multipartFile.getOriginalFilename()); }
} }
스프링에서 제공하는 MultipartFile 이라는 인터페이스를 이용해서, HTTP multipart 요청을 처리한다. MultipartFile 요청은 큰 파일을 청크 단위로 쪼개서 효율적으로 파일 업로드 할 수 있게 해준다. MultipartFile 인터페이스를 열어보면 다음과 같이 나와있다.
A representation of an uploaded file received in a multipart request.
이제, 다시 본로으로 돌아와서 실제 위의 작성한 코드를 살펴보면 Paths.get() 메서드를 통해서 저장할 위치(copyOfLocation)을 지정한다. 여기서 중요한 것은 File.seperator 가 OS 종속적으로 mac이나 리눅스 계열에서는 / 윈도우에서는 \ 구분자를 나타낸다. 또한, StringUtils.cleanPath를 사용한다.
그리고, Files.copy()메서드를 통해서 multipartFile의 inputstream을 얻어서 위에서 지정한 copyOfLocation에 파일을 쓴다. 정확히 Files.copy()메서드의 정의를 살펴보면 다음과 같다.
Copies all bytes from an input stream to a file. On return, the input stream will be at end of stream. input stream으로 얻은 모든 bytes 들을 file로 copy한다.
여기서는 form 태그에 input type을 file로 받아서, form을 submit하면 FileController에 정의했던 API를 타게 된다. 중요한 건 input의 name를 file 로 한 부분이 FileController 의 @ReuqestParam("file") 이 부분과 일치해야 한다.