Spring에서 file 다루기 (MultipartFile)

Sorbet·2021년 12월 24일
4

1) 인트로

  • 스프링부트 서버에서 파일을 다룰때 편하게 쓸 수 있는 인터페이스 MultipartFile 를 간단히 사용해 보려고 합니다
  • 테스트를 위해서 Spring MVC && thymleafe 로 페이지를 구성했는데, 프론트 코드는 HTML이 대부분이라 다른 FE 기술스텍에서도 어렵지않게 사용 가능합니다.
  • 이번 글에서는 minimal 한 글을 위해서 Client에게 파일 딱 하나만 업로드받아 서버 $Home 디렉토리에 저장하겠습니다

2) 전체구조

[front-end]
1) 파일 전송 요청
- 뷰 페이지
- "/upload"




[back-end]
2) 요청 받아서 파일을 서버에 저장
- Post로 파일전송받음
- "api/v1/file"
- 성공시 success페이지로 이동
- 실패시 예외발생, 에러리턴




[front-end]
3) 전송 성공 확인페이지
- 뷰 페이지
- "/success"
          

3) FileController.java

@Controller
@RequiredArgsConstructor
public class FileController {

    private final FileService fileService;

    @GetMapping("/upload")
    public String upload() {
        return "upload";
    }

    @GetMapping("/success")
    public String success() {
        return "success";
    }

    @PostMapping("/api/v1/file")
    public String uploadFile(@RequestParam("file") MultipartFile file) {
        fileService.fileUpload(file);
        return "redirect:/success";
    }
}

4) FileService

@Service
public class FileService {

    Logger log = LoggerFactory.getLogger(getClass());

    @Value("${user.home}")
    private String uploadDir;


    public void fileUpload(MultipartFile multipartFile) {
        Path serverPath = Paths.get(
                uploadDir +
                        File.separator +
                        StringUtils.cleanPath(multipartFile.getOriginalFilename()));

        try {
            Files.copy(multipartFile.getInputStream(), serverPath, StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
            log.error("fail to store file : name={}, exception={}",
                      multipartFile.getOriginalFilename(),
                      e.getMessage());
            throw new FileStorageException("fail to store file");
        }
    }

}
  • FileSystem이 OS 별로 다른 이유로 File.seperator 또한 다른데

    • mac & linux에선 / 문자인 반면, 윈도우에서는 \ 구분자이므로 StringUtils.cleanPath 를 사용해 OS 종속성 제거 했습니다
  • MultipartFile 이라는 인터페이스를 사용하는거 빼면 Local 개발컴퓨터에서 그냥 File 입출력이랑 다를게 없습니다

    • multipartFile의 inputstream을 얻어서 파일을 네트워크로 ByteStream으로 전송받아 로컬에 그대로 작성합니다.

5) TMI

굳이 MVC 까지 써야하는 이유가 있음?

  • 페이지의 form 요소의 코드 중 아래와 같은 부분이 있는데

     .... enctype="multipart/form-data">
        <input type="file" name="file" ...
  • enctype이 "multipart/form-data" 인 경우 파일로 인식해서 SpringBoot 에서 알아서 MultipartFile 객체로 변환해주기 때문

  • Content-type 타입을 명시하고(예를 들어 text이면 text/plain, xml이면 text/xml, jpg이미지는 image/jpeg 등등)

  • Body의 해석 방식을 지정하는 타입으로 enctype 을 사용하는데, 대부분의 HTTP message가 ASCI 기반 인데

  • Filed의 경우 ASCI 방식의 인코딩이 아닌, Binary 방식의 인코딩을(사진, 음악, 암호화된 문서 등등)꼭 사용해야하며 그렇지 않는경우 파일이 깨질 수 있다

    • form 에서 파일과 함께 전송하는 경우는 multipart/form-data 방식
  • multipart 타입을 지정하면 HTTP Request에서 multipart의 Body를 전송하는 규약대로 서버-클라간 통신한다

  • 자세한 사항은 RFC 7231, section 4.3.3: POST 표준을 참고해주세요

  • 아무튼 결론은 HTTP 표준의 multipart 규약을 이용하여 파일 업로드를 구현하는 것!

굳이 Console 로만 돌리고싶다면..

profile
Sorbet is good...!

0개의 댓글