Javascript의 fetch api로 요청을 보내 서버에서 보내오는 파일을 다운로드하는 기능을 구현하고자 한다.
0. fetch api로 요청 보내기
async function download(){
// 서버에 요청을 전송
const res = await fetch('/test/download');
// 파일 이름 추출
const header = await res.headers.get('content-disposition');
const fileName = await header.substring(header.indexOf('"') + 1,
header.lastIndexOf('"'));
// 응답 데이터를 blob 타입으로 변환
const blob = await res.blob();
// 주어진 객체를 가리키는 URL을 DOMString으로 변환
const url = URL.createObjectURL(blob);
// 파일을 다운로드 받기 위해 임시로 a 태그를 생성
const tempTag = document.createElement('a');
tempTag.href = url;
tempTag.download = fileName; // 파일명 설정
// a 태그를 document에 추가하고 클릭
document.body.appendChild(tempTag);
tempTag.click();
// 사용이 끝난 a 태그를 삭제하고 URL을 무효화
tempTag.remove();
window.URL.revokeObjectURL(url);
}
fetch를 사용해 서버에 요청을 보낸다.
Get 방식으로 서버에 요청으로 보내므로 fetch의 구문은 매우 단순하다. 다만 파일을 다운로드 받는 부분은 조금 복잡하다.
// 파일 이름 추출
const header = await res.headers.get('content-disposition');
const fileName = await header.substring(header.indexOf('"') + 1,
header.lastIndexOf('"'));
우선 응답 데이터에서 파일명을 추출한다. 이 작업은 fetch에서 응답 데이터를 받은 후에 수행해야하므로 await 키워드를 사용하여 순서를 기다리도록 한다.
// 응답 데이터를 blob 타입으로 변환
const blob = await res.blob();
// 주어진 객체를 가리키는 URL을 DOMString으로 변환
const url = URL.createObjectURL(blob);
그 다음 응답 데이터를 blob 타입으로 변환하고 다운로드를 하기 위해 createObjectURL() 을 통해 URL을 생성한다.
// 파일을 다운로드 받기 위해 임시로 a 태그를 생성
const tempTag = document.createElement('a');
tempTag.href = url;
tempTag.download = fileName; // 파일명 설정
// a 태그를 document에 추가하고 클릭
document.body.appendChild(tempTag);
tempTag.click();
blob에서 얻은 URL을 사용해 a 태그를 생성한다. a 태그의 href 속성에 해당 파일의 URL로 연결하고 download 속성으로 파일명을 설정한다. 설정이 끝났으면 click() 함수로 a 태그를 클릭 처리 한다.
// 사용이 끝난 a 태그를 삭제하고 URL을 무효화
tempTag.remove();
window.URL.revokeObjectURL(url);
사용이 끝난 a 태그를 삭제하고 blob로부터 얻은 URL을 무효화한다. createObjectURL() 로 생성된 URL의 경우 무효화하지 않으면 메모리 누수가 발생할 수 있으므로 반드시 revokeObjectURL() 로 URL을 해제해야한다.
1. 서버에서의 처리
서버는 일반적인 다운로드 처리 방법과 동일하다.
@GetMapping("/test")
public ResponseEntity<Resource> test(){
// 대상 파일을 지정
File file = new File("C:\\upload\\test.jpg");
Resource resource = new FileSystemResource(file);
String filename = resource.getFilename();
// 헤더의 Content Type을 application/octet-stream 으로 설정
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
try {
// 헤더에 content-disposition 항목을 설정한다.
// content-disposition에는 attachment 와 filename 이 포함되어야 한다.
headers.add("content-disposition",
"attachment;filename=\""
+ new String(filename.getBytes("UTF-8"), "ISO-8859-1") + "\"");
} catch (Exception e) {
e.printStackTrace();
}
return new ResponseEntity<Resource>(resource, headers, HttpStatus.OK);
}
* Content-Disposition
HTTP Response Body에 담기는 컨텐츠가 inline로 표시될지 attachment로 표시될지 여부를 나타낸다.
inline은 컨텐츠가 웹페이지나 웹페이지의 일부로서 표시된다.
attachment는 컨텐츠가 다운로드 되어 사용자의 컴퓨터에 저장된다.
그중 attachment의 filename은 다운로드되는 파일의 이름을 나타내는 역할을 한다.
'JavaScript > JavaScript' 카테고리의 다른 글
[Javascript] FormData 객체의 사용 (0) | 2023.02.28 |
---|---|
[Javascript] 자바스크립트로 Form 초기화하기 (0) | 2023.02.27 |
[Javascript] async와 await (0) | 2023.02.09 |
[Javascript] 비동기 통신을 위한 3가지 방법 (0) | 2023.02.09 |
[Javascript] Modal 창 조작하기 (0) | 2023.02.08 |