Framework/Spring

Spring 첨부파일 다운로드 구현하기

MINGYUM 2022. 3. 5. 19:12

첨부파일이 이미지인 경우, 클릭했을 때 화면에 크게 원본 파일을 넘겨줘야 하고,

일반 첨부파일인 경우에는 다운로드를 기본으로 실행해야 한다. 

 

MIME 타입이란? 

https://developer.mozilla.org/ko/docs/Web/HTTP/Basics_of_HTTP/MIME_types

 

MIME 타입 - HTTP | MDN

MIME 타입이란 클라이언트에게 전송된 문서의 다양성을 알려주기 위한 메커니즘입니다: 웹에서 파일의 확장자는 별  의미가 없습니다. 그러므로, 각 문서와 함께 올바른 MIME 타입을 전송하도

developer.mozilla.org

이런 종류가 있다.

다운로드 시 MIME 타입은 고정되기 때문에 메서드는 아래와 같이 시작하게 된다.

    @GetMapping(value = "/download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
    @ResponseBody
    public ResponseEntity<Resource> downloadFile(String fileName){
        Resource resource = new FileSystemResource("C:\\upload\\" + fileName);

        return null;
    }

MIME 타입은 다운로드를 할 수 있는 application/octet- stream 으로 지정한다. 

 

springframeword.core.io.Resource 인터페이스는 FileSystemResource, UriResource 등 다양한 리소스를 의미한다. 

 

이제 HttpHeaders 객체를 이용해서 다운로드 시 파일 이름을 지정할 수 있다. 

    @GetMapping(value = "/download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
    @ResponseBody
    public ResponseEntity<Resource> downloadFile(String fileName){
        Resource resource = new FileSystemResource("C:\\upload\\" + fileName);

        String resourceName = resource.getFilename();
        HttpHeaders headers = new HttpHeaders();
        try {
            headers.add("Content-Disposition", "attachment; filename="
                    + new String(resourceName.getBytes(StandardCharsets.UTF_8), "ISO-8859-1"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return new ResponseEntity<Resource> (resource, headers, HttpStatus.OK);
    }

또한 Content-Disposition이라는 headerName을 사용해서, 다운로드 시 저장되는 이름을 지정한다.

https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/Content-Disposition 

 

Content-Disposition - HTTP | MDN

multipart/form-data 본문에서의 Content-Disposition 일반 헤더는 multipart의 하위파트에서 활용될 수 있는데, 이 때 이 헤더는 multipart 본문 내의 필드에 대한 정보를 제공합니다. multipart의 하위파트는 Conte

developer.mozilla.org

Content-Disposition 응답 헤더는 콘텐츠가 첨부파일로 표시되는 지 여부를 나타내는 헤더이다. 

다운로드 되어 로컬에 저장됨.

 

인코딩 처리도 해주어 한글 입력시 깨짐 현상을 방지한다. 

 

추가적으로, Edge에서 다운로드 시 깨짐현상을 방지해야 한다. 

HTTP 헤더 메세지 중 User-Agent 값을 이용해 브라우저의 프로그램이 종류를 구분할 수 있다. 

    @GetMapping(value = "/download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
    @ResponseBody
    public ResponseEntity<Resource> downloadFile(@RequestHeader("User-Agent")String userAgent,  String fileName){
        Resource resource = new FileSystemResource("C:\\upload\\" + fileName);

        if (resource.exists() == false) {
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        }

        String resourceName = resource.getFilename();
        HttpHeaders headers = new HttpHeaders();
        try {
            String downloadName = null;
            if(userAgent.contains("Trident")){
                log.info("IE browser");
                downloadName = URLEncoder.encode(resourceName, "UTF-8").replaceAll("\\+", " ");

            } else if (userAgent.contains("Edge")) {
                log.info("Edge browser");
                downloadName = URLEncoder.encode(resourceName, "UTF-8");
            } else{
                log.info("Chrome browser");
                downloadName = new String(resourceName.getBytes("UTF-8"), "ISO-8859-1");
            }
            headers.add("Content-Disposition", "attachment; filename=" + downloadName);

        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return new ResponseEntity<Resource> (resource, headers, HttpStatus.OK);
    }

서버에서 파일 이름에 UUID가 붙은 부분을 제거하고 순수하게 파일 이름으로 저장될 수 있도록 한다.