yahayaha

28. 파일 다운로드(브라우저 처리) 본문

spring/프로젝트

28. 파일 다운로드(브라우저 처리)

yaha 2024. 2. 19. 23:38

첨부파일의 다운로드는 서버에서 MIME 타입을 다운로드 타입으로 지정하고, 적절한 헤더 메시지를 통해서 다운로드 이름을 지정하게 처리함.

 

다운로드는 MIME 타입이 고정되기 떄문에 메서드를 작성해줘야함.

 

@GetMapping(value ="/download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
@ResponseBody
public ResponseEntity<Resource> downloadFile(String fileName){
    log.info("다운로드 파일 : " + fileName);

    FileSystemResource resource = new FileSystemResource("C:\\upload\\" + fileName);

    log.info("resource" + resource);

    return null;
}

 

테스트 하기 위해서 브라우저에서 /download?fileName=파일이름 형태로 호출을 진행.

 

favicon은 따로 수정예정.

 

서버에서 파일이 정삭적으로 인식이 된걸 확인. 이제 ResponseEntity<>를 처리해야함.

 

HttpHeaders 객체를 이용해서 다운로드 시 파일의 이름을 처리하도록함.

 

@GetMapping(value ="/download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
@ResponseBody
public ResponseEntity<Resource> downloadFile(String fileName){
    log.info("다운로드 파일 : " + fileName);

    // 다운로드할 파일의 경로를 지정하여 Resource 객체 생성
    Resource resource = new FileSystemResource("C:\\upload\\" + fileName);

    log.info("resource" + resource);

    // 다운로드할 파일의 이름을 가져옴
    String resourceName = resource.getFilename();

    // HTTP 응답 헤더 설정
    HttpHeaders headers = new HttpHeaders();

    try {
        // 다운로드할 파일의 이름을 UTF-8에서 ISO-8859-1로 변환하여 헤더에 추가
        headers.add("Content-Disposition",
                "attachment; filename=" + new String(resourceName.getBytes("UTF-8"),
                "ISO-8859-1"));
    } catch (UnsupportedEncodingException  e) {
        // 예외 발생 시 오류 출력
        e.printStackTrace();
    }

    // 다운로드할 파일의 Resource와 설정된 헤더를 이용하여 ResponseEntity 생성하여 반환
    return new ResponseEntity<Resource>(resource, headers, HttpStatus.OK);
}

 

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

 

다운로드 시 저장되는 이름은 Content-Disposition을 이용해서 지정함.

 

파일 이름에 대한 문자열 처리는 파일 이름이 한글인 경우 저장할 때 깨지는 문제를 막기 위함.

 

크롬 브라우저에서 C:\upload 폴더에 잇는 파일의 이름과 확장자로 '/download?fileName=xxxx와 같이 호출하면 브라우저는 자동으로 해당 파일을 다운로드하는 것을 볼 수 있음.

 

IE 인터넷 익스플로러 경우 파일 다운로드가 호출이 안되는 문제가 있음. 이에 대한 문제는 뒤에서 해결하기로함.

 

일단 브라우저에 다운로드 테스트를 하기위해 '/download?fileName=xxxx 던져봄

 

 

영어와 한글 모두 다 다운로드 되는게 확인

 

IE와 Edge 브라우저에서는 한글 이름이 일단 내 컴퓨터 상으로는 제대로 한글까지 출력이 되는걸 확인 했지만.

 

혹시 모르니 IE와 Edge 브라우저에서 문제가 있을 수 있으니 한글 이름이 제대로 나오는 코드를 추가.

 

Content-Disposition의 값을 처리하는 방식이 IE의 경우 인코딩 방식이 다르다고 함.

 

IE를 같ㅇ ㅣ서비스 해야하면 HttpServletRequest에 포함된 헤더 정보들을 이용해서 요청이 발생한 브라우저가

IE계열인지 확인해서 다르게 처리하는 방식으로 처리.

 

HTTP 헤더 메세지 중에서 디바이스의 정보를 알 수 있는 헤더는 User-Agent 값을 이용.

 

이에대한 downloadFile 수정이 필요함.

 

@GetMapping(value ="/download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
@ResponseBody
public ResponseEntity<Resource> downloadFile(@RequestHeader("User-Agent") String userAgent, String fileName){
    log.info("다운로드 파일 : " + fileName);

    // 다운로드할 파일의 경로를 지정하여 Resource 객체 생성
    Resource resource = new FileSystemResource("C:\\upload\\" + fileName);

    log.info("resource" + resource);

    // 파일이 존재하지 않는 경우 404 응답 반환
    if(resource.exists() == false) {
        return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }

    // 다운로드할 파일의 이름을 가져옴
    String resourceName = resource.getFilename();

    // HTTP 응답 헤더 설정
    HttpHeaders headers = new HttpHeaders();

    try {

        //다운로드될 파일의 이름을 저장하는 변수를 선언하기위해 초기화
        String downloadName = null;

        // User-Agent 헤더를 기반으로 브라우저 종류를 파악하여 파일 이름 인코딩 처리
        if(userAgent.contains("Trident")) {

            log.info("IE 브라우저");

            // IE 브라우저인 경우 파일 이름을 UTF-8로 인코딩한 후 공백을 +로 치환하여 설정
            downloadName = URLEncoder.encode(resourceName, "UTF-8").replaceAll("\\+", " ");
        }else if(userAgent.contains("Edge")) {

            log.info("엣지 브라우저");

            // Edge 브라우저인 경우 파일 이름을 UTF-8로 인코딩하여 설정
            downloadName = URLEncoder.encode(resourceName, "UTF-8");

            log.info("엣지 name : " + downloadName);
        }else {

            log.info("크롬 브라우저");

            // 그 외의 브라우저인 경우(나는 일단 크롬만 쓸거니까 로그에는 크롬 브라우저만) 파일 이름을 ISO-8859-1로 인코딩하여 설정
            downloadName = new String(resourceName.getBytes("UTF-8"),"ISO-8859-1");
        }

        headers.add("Content-Disposition", "attachment; fileName=" + downloadName);


    } catch (UnsupportedEncodingException  e) {
        // 예외 발생 시 오류 출력
        e.printStackTrace();
    }

    // 다운로드할 파일의 Resource와 설정된 헤더를 이용하여 ResponseEntity 생성하여 반환
    return new ResponseEntity<Resource>(resource, headers, HttpStatus.OK);
}

 

downloadFile은 @RequestHeader를 이용해서 필요한 HTTP 헤더 메세지의 내용을 수집할 수 있음.

 

이를 이용해서 User-Agent의 정보를 파악하고 값이 "MSIE"혹은 Trident(IE 브라우저의 엔진 이름)인 경우 다른 방식으로 처리하도록 함.

 

일단 이걸 하기 전부터 내 컴퓨터에는 IE나 Edge에서 한글 파일 다운로드가 잘 되어있어서 따로 테스트는 실행하지 않음. 브라우저 업데이트로 인한건지 확인이 필요함.

 

크롬같은 경우 MSIE와 Trident 엔진이 아니라서 크롬 브라우저가 나오는걸 확인 가능함.

 

----------------------------------------------------------------------------

 

하다 보니까 뭔가 이상해서 현재 오류가 진짜 없나? 찾아보는중 뭔가 이상함.