서버 코드 살펴보기
1. 헤더의 Content-Type을 application/octet-stream으로 설정하기
application/octet-stream은 8비트로 된 데이터라는 뜻으로 response 데이터가 binary 파일이라는 것을 암시합니다.
2. Content-Disposition 헤더의 속성값을 attached로 입력하고, filename도 전달하기
Content-Disposition는 Response Body의 콘텐츠를 브라우저에 인라인으로 표시할지 여부를 결정하는 헤더입니다.
속성값에 따른 차이는 아래와 같습니다.
Content-Disposition: inline // 콘텐츠를 브라우저 화면에 표시합니다.
Content-Disposition: attachment // 콘텐츠를 다운로드 받습니다.
Content-Disposition: attachment; filename="filename.jpg" // 파일명을 지정
3. 헤더의 Content-Transfer-Encoding 속성값을 binary로 입력하기
Content-Transfer-Encoding 헤더는 binary 메시지를 텍스트 포맷으로 변형시키는 인코딩 방법을 정의합니다.
속성값을 binary로 기재하면 Response 데이터가 binary 데이터라는 것을 암시합니다.
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=\"" + fileName);
response.setHeader("Content-Transfer-Encoding", "binary");
프론트엔드 코드 살펴보기
1. axios 요청 시 responseType을 "blob"로 설정하기
Response Data가 binary Blob로 반환되어야 함을 나타내줍니다.
axios({
method: "get",
url: `${serverURL}/api/download/`,
responseType: "blob",
});
*Blob (Binary Large OBject)란?
Blob는 JavaScript에서 binary 데이터를 나타내는데 사용되는 데이터 구조입니다.
이미지, 비디오 등 대용량 데이터를 저장하고 조작할 수 있습니다.
Blob 데이터는 Blob 생성자로 만들 수 있으며 다음을 포함한 여러 방법으로 사용할 수 있습니다.
- binary 데이터를 DB 또는 서버에 Blob로 저장합니다.
- 서버에서 binary 데이터를 다운로드하고 사용자를 위한 다운로드 링크를 생성합니다. (2번에서 사용)
- binary 데이터를 기타 다른 형식의로 변환합니다.
2. Blob를 통해 다운로드 URL을 생성합니다.
*window.URL.createObjectURL(object) 메소드
: 매개변수 object를 나타내는 URL 문자열을 생성합니다. object에는 파일 또는 Blob 객체가 옵니다.
const downloadUrl = window.URL.createObjectURL(new Blob([res.data]));
3. Content-Disposition 헤더를 통해 파일명을 추출합니다.
const filename = res.headers["content-disposition"].split("filename=")[1];
4. <a>를 만들어 데이터 다운로드를 진행합니다.
DOM 문법을 사용하여 <a>태그를 생성하고, href 속성으로 다운로드 링크를 연결합니다.
download 속성값으로 위에서 추출한 filename을 입력합니다.
download 속성이 있어야 해당 링크로 이동하지 않고 파일을 다운로드 받게 됩니다.
<a>를 body 태그에 append로 연결하고, click이벤트를 발생시켜 파일을 다운로드 받습니다.
이후 해당 엘리먼트를 제거해줍니다.
const link = document.createElement("a");
link.href = downloadUrl;
link.setAttribute("download", filename);
document.body.appendChild(link);
link.click();
link.remove();
5. 전체 코드
axios({
method: "get",
url: `${serverURL}/api/download/`,
responseType: "blob",
})
.then((res) => {
const downloadUrl = window.URL.createObjectURL(new Blob([res.data]));
const filename = res.headers["content-disposition"].split("filename=")[1];
const link = document.createElement("a");
link.href = downloadUrl;
link.setAttribute("download", filename);
document.body.appendChild(link);
link.click();
link.remove();
})
.catch((error) => {
alert("파일 다운로드중 문제가 발생하였습니다.");
});
에러핸들링 : Response Header에 Content-Disposition이 없는 경우
개발자도구의 응답 헤더에는 Content-Disposition이 있었습니다.
그러나 Axios의 Response 객체에는 해당 데이터가 담겨있지 않았고, 파일명을 추출하는데 문제가 생겼습니다.
원인은 CORS 이슈이며 서버에서 에러핸들링을 해야합니다.
// SpringBoot
@CrossOrigin(value = {"*"}, exposedHeaders = {"Content-Disposition"})
'프로그래밍 > 웹 개발' 카테고리의 다른 글
[React] 컴포넌트 강제 리렌더링 (0) | 2023.05.09 |
---|---|
[React, TypeScript] 브라우저 화면에서 특정 단어 검색 기능 구현 (0) | 2023.04.20 |
[React] Button 통한 파일 업로드 및 진행상황(progress) 표시 (0) | 2023.04.12 |
[react-color, TypeScript] 컬러 선택 기능 구현하기 (color picker) (0) | 2023.04.08 |
[SCSS, JS] JavaScript를 통해 SCSS 속성값 동적으로 연결하기 (0) | 2023.04.08 |
댓글