[Spring Boot]FileUpload 구현시 필요한 REST API의 개념과 적용방법

웹 플렛폼 제작시 필요한 파일 업로드 기능을 이해해보자

REST

정의

REST는 Representational State Transfer라는 용어의 약자로서 자원의 이름(자원의 표현)으로 구분하여 해당 자원의 상태(정보)를 주고 받는 모든 것을 의미한다.

월드 와이드 웹과 같은 분산 하이퍼미디어 시스템을 위한 소프트웨어 개발 아키텍처의 한 형식이다.

REST는 기본적으로 웹의 기존 기술과 HTTP 프로토콜을 그대로 활용하기 때문에 웹의 장점을 최대한 활용할 수 있는 아키텍처 스타일이다.

구체적인 개념

HTTP URI(Uniform Resource Identifier)를 통해 자원을 명시하고 HTTP Method(POST, GET, PUT, DELETE)를 통해 해당 자원에 대한 CRUD Operation을 적용하는 것을 의미한다.

REST API

API

API(Application Programming Interface)란 데이터와 기능의 집합을 제공하는 컴퓨터 프로그램간 상호작용을 촉진하며, 서로 정보를 교환 가능하도록 한 것이다.

그러므로 REST API는 REST기반의 서비스 API를 구현한 것이다. 파일 업로드 기능 또한 이를 이용한 것이다.

적용 (Upload)

사용자가 페이지에서 file-upload form으로 원하는 파일을 업로드 한다.

fileUpload.html

1
2
3
4
5
6
7
<div>
	<form method="POST" enctype="multipart/form-data"  action="#" 
		th:action="@{'/project/'+${nickname}+'/'+${title}+'/cloud/upload'}">
			<tr><td>File to upload:</td><td><input type="file" name="file" /></td></tr>
			<tr><td></td><td><input type="submit" value="Upload" /></td></tr>
	</form>
</div>

<form enctype="속성값"> 태그의 enctype 속성은 폼 데이터가 서버로 제출될 때 해당 데이터가 인코딩되는 방법을 명시한다.

이때 속성값인 multipart/form-data 는 모든 문자를 인코딩하지 않음을 명시하며 해당 요소가 파일이나 이미지를 서버로 전송할 때 사용한다.

POST 요청이 들어가면 이 주소에 맞는 컨트롤러로 MultipartFile 객체가 전송된다.

UploadFilecontroller.java

1
2
3
4
5
6
7
8
9
10
11
12
13
@PostMapping("/project/{nickname}/{title}/cloud/upload")
public String handleFileUpload(@PathVariable("nickname") String nickname, @PathVariable("title") String title,
            @RequestParam("file") MultipartFile file, @CurrentUser Account account, RedirectAttributes redirectAttributes) {

    // nickname과 projectTitle로 projectId 찾기
    Long projectId = projectService.getProjectId(nickname, title);

    uploadFileService.storeFile(file,projectId,account);
    redirectAttributes.addFlashAttribute("message",
            "You successfully uploaded " + file.getOriginalFilename() + "!");

    return "redirect:/project/"+nickname+"/"+title+"/cloud";
}

@RequestParam(“file”) 어노테이션을 사용하여 form에서 전송한 데이터를 MultipartFile file로 받는다.

받아온 file과 파일이 업로드된 프로젝트의 id값, 해당 유저의 정보를 구현해 둔 uploadFileService로 보내 DataBase 내부에 저장해준다.

UploadFileService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void storeFile(MultipartFile file, Long projectId, Account uploader){
    String fileName = StringUtils.cleanPath(file.getOriginalFilename());

    try{
        if(fileName.contains("..")){
            throw new FileStorageException("invalid path : "+fileName );

        }

        UploadFile uploadFile = new UploadFile(projectId, uploader, fileName,
                                        file.getContentType(), file.getBytes());
        

        uploadFileRepository.save(uploadFile);
    } catch (IOException ex){
        throw new FileStorageException("Could not store file " + fileName + ". Please try again!", ex);
    }
}

StringUtils

String fileName = StringUtils.cleanPath(file.getOriginalFilename()); StringUtils는 자바의 String 클래스가 제공하는 문자열 관련 기능을 강화한 클래스이다. StringUtils 클래스만으로 거의 대부분의 문자열 처리를 수행할 수 있으며 피라미터 값으로 null을 주더라도 절대 NPE를 발생시키지 않는다.

여기서 cleanPath()메서드는 String 형태로 넘어오는 path를 정규화 시켜준다. 쉽게 말하면 “\“가 “/”로 변환되는 것이다.

이후 파일 경로를 확인한 뒤 문제가 없다면 DB에 저장될 객체를 생성하고 JPA로 저장해준다.

적용 (DownLoad)

ResponseEntity.java

1
2
3
4
5
6
7
8
9
10
11
@GetMapping("/project/{nickname}/{title}/cloud/download/{fileId}")
public ResponseEntity<Resource> downloadFile(@PathVariable("fileId") String fileId) {
    // Load file from database
    
    UploadFile uploadFile = uploadFileService.getFile(Long.parseLong(fileId));

    return ResponseEntity.ok()
            .contentType(MediaType.parseMediaType(uploadFile.getFileType()))
            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + uploadFile.getFileName() + "\"")
            .body(new ByteArrayResource(uploadFile.getData()));
}

DB에 저장된 file의 id값으로 URL을 할당하고 해당 주소로 GET요청이 들어오면 해당 메서드가 동작한다.

ResponseEntity

ResponseEntity는 @ResponseBody 어노테이션과 같은 의미로, ResponseEntity를 return Type으로 지정하면 JSON 또는 XML Format으로 결과를 내려준다.

우리는 사용자가 다운로드하는 파일의 타입과 이름, 파일자체를 전송해준다.

body에 담기는 ByteArrayResource는 byte array로 부터 사용 가능한 파일을 얻어낸다.

Reference

https://gmlwjd9405.github.io/2018/09/21/rest-and-restful.html

http://tcpschool.com/html-tag-attrs/form-enctype

https://smallgiant.tistory.com/68

https://docs.spring.io/spring/docs/3.0.6.RELEASE_to_3.1.0.BUILD-SNAPSHOT/3.1.0.BUILD-SNAPSHOT/org/springframework/web/multipart/MultipartFile.html

https://devfunny.tistory.com/321

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/io/ByteArrayResource.html