[Spring/React] 파일 다운로드

bin·2023년 6월 1일
0

우선 스프링 프로젝트 내부에 파일 업로드 시 업로드한 파일이 저장되는 폴더인 upload 폴더가 있다고 가정해보자.

C:\sample-project\src\main\resources\static\upload

해당 upload 폴더에 저장되어 있는 star.jpg 파일을 다운로드 하는 로직은 다음과 같다.

BackEnd - Spring

VO

public class FileVO {
    
    /** 첨부파일 아이디 */
    private Long fileId;

    /** 게시글 아이디 */
    private Long postId;

    /** 파일 경로명 */
    private String filePathNm;

    /** 논리 파일명 */
    private String logicFileNm;

    /** 확장자 */
    private String extension;

    /** 등록일시 */
    private String regDate;

    /** 파일명 **/
    private String fileNm;

    ...getter/setter
}

Service

    @Override
    public void downloadFile(Long postId, HttpServletRequest request, HttpServletResponse response) throws Exception {
        FileVO fileInfo = fileDAO.selectFileInfo(postId); // 게시글 id가 담긴 

        String systemPath = request.getSession().getServletContext().getRealPath("/") + ".."; // C:\sample-project\src\main\resources\static\upload
        String filePath = systemPath + fileInfo.getFilePathNm(); // C:\sample-project\src\main\resources\static\upload/star.jpg
        String fileNm = fileInfo.getFileNm(); // star.jpg

        File file = new File(filePath); // 다운로드 될 파일 객체 생성

        byte fileByte[] = FileUtils.readFileToByteArray(file); // 파일을 Byte 배열로 변환
        response.setContentType("application/octet-stream"); // octet-stream: 데이터가 8비트 바이너리 배열임을 의미
        response.setContentLength(fileByte.length);

        // Content-Disposition에 attachment를 주는 경우, Body에 오는 값을 다운로드 받으라는 의미, fileName은 다운로드 될 파일명 
        response.setHeader("Content-Disposition", "attachment; fileName=\"" + URLEncoder.encode(fileNm,"UTF-8") +"\""); 
        
        // 전송 data의 인코딩 방식 설정
        response.setHeader("Content-Transfer-Encoding", "binary");

		// 데이터를 임시로 모아둔 버퍼의 출력 스트림을 출력
        response.getOutputStream().write(fileByte);
        
        response.getOutputStream().flush(); // 버퍼 비우기 
        response.getOutputStream().close(); // response 닫기 
    }

FrontEnd - React

Axios로 요청하여 다운로드 받는 로직이다.

    const download = async(post) => {
        Axios.post('/api/sample/downloadFile', 
            {
                postId: post.postId
            },
            {
                responseType: 'blob', // 파일 다운로드를 위해 responseType을 'blob'으로 설정 -> response.data는 blob 객체가 된다.
            }
        ).then((response) => {
          	// 응답 header에서 fileName 잘라내기
            const name = response.headers["content-disposition"]
                .split("fileName=")[1]
                .replace(/"/g, "");
          	
          	// 응답 데이터인 blob 객체 URL 생성
            const url = window.URL.createObjectURL(response.data); 
          	
          	// 응답 데이터인 blob 객체 URL을 설정할 링크 생성
            const link = document.createElement("a"); 
            link.href = url;
            link.setAttribute("download", name);
            link.style.cssText = "display: none";
          
          	// 링크를 body에 추가
            document.body.appendChild(link); 
          
          	// 강제로 click 이벤트 발생시키기
            link.click(); 
            link.remove(); 
        })
    }

0개의 댓글