Back-End/REST with spring boot basic

12. file upload

728x90

프로토콜

파일을 업로드하는 메서드는 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–

코드 구현

파일을 저장할 위치를 먼저 프라퍼티에 지정한다.(application.properties)

upload.root_folder = c:/temp/image
image_folder = /assets/upload

프라퍼티 정보를 읽어서 저장할 빈 객체를 생성한다. main 애플리케이션이 있는 위치에 ConfigConstant.java 화일을 생성한다.

@Component
public class ConfigConstant {
    @Value("${upload.root_folder}")
    public String uploadRootFolder;
 
    @Value("${image_folder}")
    public String image_folder;
}

@Component 어노테이션을 주었으므로 이 빈 객체도 스프링이 구동되면 스프링 컨테이너에 인스턴스가 등록이 된다.

 

HeroController에 post 메서드를 작성한다. /api/file 프로토콜로 작성한다.

먼저, 컨테이너에 등록된  ConfigConstant 인스턴스를 주입받는다.

@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가 사용된다.