프로토콜
파일을 업로드하는 메서드는 Post 이다. 그런데 Content-Type이 좀 독특하다.
Request할때 헤더를 fiddler를 사용해서 살펴보면,
Content-Type: multipart/form-data; boundary=abcdefghhhhhhhhhhhh
body에 보내는 데이터의 content type이 form-data이긴 한데 여러부분으로 나누어 보낸다는 multipart 형태이다. 그리고 각각의 part 사이는 boundary를 이용해서 구분하는데, 앞에 –를 붙인다. 그리고 맨 마지막에 끝날때는 다시 –를 붙인다. 예를 들어 3 part를 전송한다고 가정하면 아래와 같다.
–abcdefghhhhhhhhhhhh
one part
…
–abcdefghhhhhhhhhhhh
two part
…
–abcdefghhhhhhhhhhhh
three part
…
–abcdefghhhhhhhhhhhh–
코드 구현
파일을 저장할 위치를 먼저 프라퍼티에 지정한다.
1 2 |
upload.root_folder = c:/temp/image image_folder = /assets/upload |
프라퍼티 정보를 읽어서 저장할 빈 객체를 생성한다. main 애플리케이션이 있는 위치에 ConfigConstant.java 화일을 생성한다.
1 2 3 4 5 6 7 8 |
@Component public class ConfigConstant { @Value("${upload.root_folder}") public String uploadRootFolder; @Value("${image_folder}") public String image_folder; } |
@Component 어노테이션을 주었으므로 이 빈 객체도 스프링이 구동되면 스프링 컨테이너에 인스턴스가 등록이 된다.
HeroController에 post 메서드를 작성한다. /api/file 프로토콜로 작성한다.
먼저, 컨테이너에 등록된 ConfigConstant 인스턴스를 주입받는다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
@Autowired private ConfigConstant configConstant; @PostMapping("/file") public Result fileUpload(@RequestPart(value="file") MultipartFile file) { try { // 이미지가 있는지 체크 if (file != null) { //업로드할 디렉토리가 있는지 체크 String path = configConstant.uploadRootFolder + configConstant.image_folder; File dir = new File(path); if (!dir.isDirectory()) { dir.mkdirs(); } // 파일 저장: 파일명은 중복을 피하기 위해서 파일명 _타임스템프 String filename = file.getOriginalFilename(); String savedFilename = filename.substring(0, filename.lastIndexOf(".")) + "_" + System.currentTimeMillis() + filename.substring(filename.lastIndexOf(".")); File saveFile = new File(path, savedFilename); file.transferTo(saveFile); return new Result(0, configConstant.image_folder + "/" + savedFilename); } } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return new Result(500, "internal server error"); } |
테스트
postman으로 테스트해본다.
http://localhost:8080/api/file 을 입력하고,
body 부분에서 form-data를 선택하고 key 부분에는 file을 입력하고 파일선택 부분에 이미지하나를 선택한다.
정상적으로 업로드가 완료되면 업로드된 경로가 리턴된다.
fiddler로 패킷을 캡쳐해보면 다음과 같다.
request부분에 Content-Type 부분을 자세히 보자. multipart/form-data가 있고, 브라우저가 자동으로 만들어주는 boundary가 있다.
Content-Type: multipart/form-data; boundary=—-WebKitFormBoundarypiNIoaXgpD7xY6SC
이 boundary가 아래에는 –가 붙여서 쓰이고 맨 마지막에 다시 이 boundary가 사용된다.