728x90

이전 포스팅에서는 게시글 상세페이지에서 파일을 다운로드 하기 위해 

게시글에 해당하는 파일 정보를 불러오는 기능을 추가하였다.

 

오늘은 드디어 핵심기능인

게시글 상세페이지 내 파일을 클릭하면 다운로드를 할 수 있는 예제를 진행해보자.

 

이전 포스팅 참고

https://just-joat.tistory.com/36 

 

[Spring Boot] 스프링부트 & MyBatis 게시판 파일 다운로드 예제 (1/2)

이전 포스팅인 게시판 파일 업로드 예제에 이어서이번에는 파일을 다운로드 하는 예제를 진행해보겠다. 따라서 DB 내에 게시글 테이블 및 파일 정보 테이블이 존재하고,파일 업로드 예제의 소

just-joat.tistory.com

 

전체적인 흐름은 다음과 같다.

 

  1. 상세 페이지에서 파일을 클릭하면 해당 파일의 seq 값을 서버에 요청
  2. 요청된 서버의 seq 값을 가지고 파일 테이블에서 파일의 정보 조회
  3. 조회된 파일의 정보를 이용해 자바의 Resource 인터페이스 구현체를 생성하여 리턴

* Resource 인터페이스: 컴퓨터 내 물리적으로 저장된 파일을 읽을 수 있는 인터페이스. 해당 인터페스의 구현체를 클라이언트에게 반환하면 파일을 다운로드 받을 수 있음.

 

 

1. FileMapper 인터페이스 수정 - 게시글 내 클릭한 파일의 상세정보 조회 메서드 추가


FileResponse getFileBySeq(int seq); // 파일 상세정보 조회
  • DB 내 파일 테이블에서 파일의 상세정보를 조회 할 수 있도록 메서드를 추가한다.
  • 리턴 타입은 당연히 FileResponse 이다.

 

 

2. FileMapper XML 수정 - 파일 SEQ 값을 참조하여 파일의 정보를 조회하는 쿼리 추가


<select id="getFileBySeq" resultMap="fileResponse">
    SELECT
        SEQ,
        <include refid="fileColumns"></include>
    FROM
        BOARD_FILE
    WHERE
        DELETE_YN = 'N'
        AND SEQ = #{seq}
</select>
  • 요청된 파일의 seq 값을 이용해 해당 seq의 파일 정보를 조회하는 쿼리를 추가한다. 
  • FileResponse 로 변환하기 위해 resultMap 설정하는거 잊지말자.

 

 

3. FileService 클래스 수정 - 메서드 추가


public FileResponse getFileBySeq(int seq) {
    return fileMapper.getFileBySeq(seq);
}
  • 파일 조회 메서드를 서비스에도 추가해준다.

 

 

4. FileUtils 클래스 수정 - 클라이언트에게 제공할 Resource 구현체 생성


//파일 Resource 객체 생성
public Resource readFileAsResource(final FileResponse file) {
    String uploadedDate = file.getCreatedDate().toLocalDateTime().format(DateTimeFormatter.ofPattern("yyMMdd"));
    String filename = file.getSaveName();
    Path filePath = Paths.get(uploadPath, uploadedDate, filename);

    try {
        Resource resource = new UrlResource(filePath.toUri());
        if(resource.exists() == false || resource.isFile() == false) {
            throw new RuntimeException("file not found: "+filePath.toString());
        }
        return resource;
    } catch (MalformedURLException e) {
        throw new RuntimeException("file not found: "+filePath.toString());
    }
}
  • 앞서 말했던 이번 예제에서 가장 중요한 Resource 구현체를 생성해주는 메서드이다.
  • Path filePath = Paths.get(uploadPath, uploadedDate, filename): 앞선 과정 중 DB에서 조회하여 파라미터로 넘어온 파일의 정보를 이용해 해당 파일이 저장되어 있는 파일의 경로
  • Resource resource = new UrlResource(filePath.toUri()): 위에서 만든 파일의 경로를 Resource 인터페이스의 구현체인 UrlResource 객체 생성자에 집어넣어 결국 클라이언트에게 제공할 수 있는 Resource 객체를 만들게 된다.
  • 생성한 Resource가 존재하지 않거나 파일이 아닐 경우에 인지할 수 있는 간단한 예외처리를 한다.

 

 

5. FileRestController 클래스 수정 - 파일 다운로드 매핑 메소드 추가


// 파일 다운로드
@GetMapping("fileDownload")
public ResponseEntity<Resource> downloadFile(@RequestParam("seq") int seq) {
    FileResponse file = fileService.getFileBySeq(seq);
    Resource resource = fileUtils.readFileAsResource(file);

    try {
        String filename = URLEncoder.encode(file.getOriginalName(), "UTF-8"); // 다운로드 시 파일명
        return ResponseEntity.ok()
                .contentType(MediaType.APPLICATION_OCTET_STREAM) // HTTP MIME 타입 설정
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; fileName=\""+filename+"\";") // 헤더에 파일명 설정
                .header(HttpHeaders.CONTENT_LENGTH, file.getSize() + "") // 헤더에 파일크기 설정
                .body(resource);
    } catch (UnsupportedEncodingException e) {
        throw new RuntimeException("filename encoding failed: "+file.getOriginalName());
    }
}
  • 다운로드 요청을 받고 앞서 추가한 메서드들을 호출해서 파일의 정보를 조회하고, 조회된 파일 정보를 가지고 Resource 객체를 만들어서 클라이언트에게 반환한다.

return ResponseEntity.ok()
        .contentType(MediaType.APPLICATION_OCTET_STREAM) // HTTP MIME 타입 설정
        .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; fileName=\""+filename+"\";") // 이 응답은 파일 다운로드
        .header(HttpHeaders.CONTENT_LENGTH, file.getSize() + "") // 헤더에 파일크기 설정
        .body(resource);
  • 클라이언트에게 반환되는 이 응답은 파일 다운로드이다. 라는 것을 알려주기 위한 HTTP 헤더 설정 방법이다.
  • HttpHeaders.CONTENT_DISPOSITION: 이 값을 HTTP 헤더에 설정해주어야 클라이언트가 파일을 다운로드 받을 수 있다.
  • HTTP 바디에는 우리가 만든 Resource를 넣어준다.

 

 

※ 결과


춘식이.png 다운로드
다운로드가 잘 받아졌다.

728x90