본문 바로가기
📕 Spring Framework/Spring Project

「파일 업로드/다운로드 및 테스트」

by GroovyArea 2022. 7. 1.
이번 주에 Sonarqube를 공부하고 적용하기 전 전체적인 코드를 다시 검토 중이었다. 
빼놓은 필수적인 기능이 있다는 것을 알게 되었다. 상품 테이블에는 이미지 파일 이름 칼럼이 존재하는데 이를 깜박했던 것이다. 
파일 업로드, 다운로드 기능을 급하게 적용하게 되었고, 이 과정을 기록으로 작성하겠다.

파일 업로드

파일을 등록하는데 필요한 타입으로 스프링에서는 MultipartFile 인터페이스를 제공한다. 

이를 통해 파일 업로드 및 다운로드를 쉽게 구현할 수 있다. 

 

파일 업로드를 이용하는 내 API는 상품 추가와 수정이다.

 

먼저 파일 업로드할 위치를 내부 경로와 외부 경로중 고민을 했다. 저번 프로젝트 때는 프로젝트 내부로 지정했지만, 이렇게 하게 되면 배포 시 이미지 경로를 못 찾을 수 있다고 한다.

그래서 이번에는 프로젝트 외부 경로로 지정하기로 결정했다.

 

application.yml에 정의한 경로
Service에서 @Value 애노테이션으로 yml 값을 받아온다.

우선,

파일 업로드를 하기 위해 @RequestParam 애노테이션으로 Multipartfile 파라미터를 받아온다. 

public ResponseEntity<?> addAction(@ModelAttribute ProductDTO productDTO,
                                   @RequestParam("image") MultipartFile file) throws IOException {

 

파일 업로드는 비즈니스 로직이기 때문에 Service 클래스를 따로 선언하여 계층을 분리했다. 

public String uploadFile(MultipartFile productImageFile) throws IOException {
    String originalFileName = productImageFile.getOriginalFilename();

    UUID uuid = UUID.randomUUID();

    String uploadFileName = uuid + "_" + originalFileName;

    File file = new File(uploadDirectory, uploadFileName);

    productImageFile.transferTo(file);

    return uploadFileName;
}

=> 중복 파일명을 방지하기 위해 UUID 클래스를 이용해 업로드 할 파일명을 정의해 저장했다.

 

파일 삭제의 경우는 간단히 구현 가능하다.

public void deleteFile(String productImageName) {
    File existingFile = new File(uploadDirectory, productImageName);
    if (existingFile.exists()) {
        existingFile.delete();
    }
}

 

파일 다운로드

파일 업로드를 통해 서버에 파일을 저장시켰다. 

API를 통해 이미지를 얻으려면 전체 데이터와 함께 파일을 응답하는 것은 상당히 비효율적이라 판단을 했다.

 

그래서 기본적인 상품 데이터를 얻기 위해 Get 요청 시에는 다운로드 URI를 넘겨주고,

이 URI를 이용해 이미지를 다운로드 할 수 있게 설계했다.

 

=> 상품 조회를 하면 다운로드 URI를 넘긴다.

 

이를 통해 Get 요청을 보내보자

=> 빨간색 이미지를 업로드했으므로 이 그림을 응답받을 수 있다.

 

public Resource loadFile(String fileName) throws FileNotFoundException {
    try {
        Path directoryLocation = Paths.get(uploadDirectory)
                .toAbsolutePath().normalize();

        Path file = directoryLocation.resolve(fileName).normalize();
        Resource resource = new UrlResource(file.toUri());

        if (resource.exists() || resource.isReadable()) {
            return resource;
        } else {
            throw new FileNotFoundException(FILE_NOT_FOUNT);
        }
    } catch (MalformedURLException e) {
        throw new FileNotFoundException(FAILED_DOWNLOAD);
    }
}

=> 파일 다운로드를 위해 리소스를 반환하는 메서드

 

@GetMapping("/download/{fileName}")
public ResponseEntity<Resource> getDownloadFile(@PathVariable String fileName, HttpServletRequest request) throws IOException {
    Resource resource = fileService.loadFile(fileName);

    String contentType;

    try {
        contentType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath());
    } catch (IOException e) {
        log.error(FAILED_FILE_CONTENT);
        throw new IOException(FAILED_FILE_CONTENT);
    }

    if (contentType == null) {
        contentType = DEFAULT_CONTENT_TYPE;
    }

    return ResponseEntity.ok()
            .contentType(MediaType.parseMediaType(contentType))
            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename())
            .body(resource);
}

=> 핸들러 메서드

 

파일 업로드와 다운로드를 통해 테스트 코드를 수정했다.

단일 상품 조회

 

상품 목록 조회

반응형