프로젝트를 진행하던 중 관리자 페이지에서 첨부파일을 다운로드하는 기능을 구현해야했다.
자바스크립트의 a태그에는 download속성이 있다.
<span className={styles.file}>
{list.files?.length > 0
? list.files.map((file, index) => (
<a key={index}
href={file.fileUrl}
download={file.fielName}
rel="noreferrer">
{file.fileName}
</a>
))
: "첨부파일 없음"}
</span>
서버에서 받아오는 file의 이름을 a태그로 감싸고 a태그 속성에 href를 받아오는 파일의 url로 설정하고 download 적용해주면 다운로드가 될 줄 알았지만 단순히 file의 url링크로만 들어가지는 문제였다.
검색해보니 a태그의 download속성은 동일출처 정책에 의해 다운로드 파일의 경로가 현재 파일의 경로와 동일해야 적용된다는 것!
그니까 예를 들면 현재 사용되는 웹페이지의 url이 ' https://example.com ' 이면 다운로드 할 파일의 경로는
' https://cdn.example.com/files/abc.pdf ' 이렇게 되어야 download속성이 적용되는 것이었다.
현재 진행하고 있는 프로젝트에서 file url은 aws의 s3에서 받아오는 것이라 url이 'https://aws어쩌고 ' 이러한 경로라 안되는 것이었다.
하지만 click이벤트를 사용해 다운로드 할 수 있는 방법이 있었다.
export const useFileDownload = () => {
};
일단 다른 곳에서 사용할 수 있도록 커스텀 훅으로 만드려고 use를 설정해주고
export const useFileDownload = () => {
const fileDownload = async (url: string, name: string) => {
const res = await fetch(url);
//1. 받아오는 파일에서 url을 가져오고
const arrayBuffer = await res.arrayBuffer();
//2. fetch로 데이터를 가져온 후 arrayBuffer로 변환
const blob = new Blob([arrayBuffer], { type: "application/octet-stream" });
// 3. ArrayBuffer를 사용하여 Blob 객체를 생성.
const blobURL = URL.createObjectURL(blob);
//4. Blob 객체를 위한 객체 URL을 생성.
const a = document.createElement("a");
// 5. 다운로드를 위한 <a> 태그를 동적으로 생성.
a.href = blobURL;
//6. <a> 태그의 href 속성에 Blob URL을 설정.
a.style.display = "none";
if (name && name.length) {
a.download = name;
}
// 8. 다운로드할 파일의 이름(name)이 제공된 경우, download 속성을 설정.
document.body.appendChild(a);
//9. a태그를 바디에 추가하고
a.click();
//10. 클릭하여 다운로드 후
a.remove();
//11. 다운로드 끝나면 remove실행
};
return { fileDownload };
//fileDownload함수를 객체로 반환한다.
};
ArrayBuffer는 데이터를 메모리에 효울적으로 저장하고 다룰 때 사용된다. 주로 네트워크 통신에서 데이터를 읽어오거나 클라이언트측에서 바이너리데이터를 조작할 때 사용,
Blob은 이진 데이터를 나타내는 javaScript의 객체이다. 보통 이미지 파일은 바이너리 데이터로 구성되기 때문에 이미지를 다룰 때 클라이언트에서는 blob객체를 사용한다.
mine타입을 지정해 데이터의 종류를 브라우저에 알려주는데
{type: "application/octet-stream}
내가 사용한 이 타입은 특정한 파일 형식을 지정하지 않고 단순히 다운로드 할 바이너리 데이터에 적용할 때 사용한다.
다운로드 할 파일은 이미지, 동영상, pdf등 다양한 타입이 올 수 있기 때문!
const { fileDownload } = useFileDownload();
onClick={(e) => {
e.preventDefault();
fileDownload(file.fileUrl, file.fileName);
}}
그리고 사용할 곳에서 useFileDownload훅을 불러와 fileDownload를 할당해주고
fileDownload 인자로 파일의 url과 name을 받아주면
pdf형태의 파일이라도 적용이 되고
파일 이름을 클릭했을 때 다운로드도 잘 되는 것을 확인할 수 있다.
2024.07.20 추가)
- 로컬 http -> 서버 http
- 로컬http -> 서버 https 이 두가지 경우에는 모두 fetch함수를 사용하지 않고도 a태그의 download속성만 사용해서 다운로드가 잘 되었는데
프론트에서 서버로 쿠키가 잘 전달되지 않아 로컬을 https 로 변경한 후 로컬 https -> 서버 https, a태그 download속성만을 사용해서 첨부파일을 다운로드 하면 에러가 발생했다.
cors 에러인데 해결했던 방법은 단순히 서버에서 저장소를 디스크로 바꾸니까 https -> https에서도 a태그 download속성만을 사용해도 다운로드가 잘 되었다.
access -control- allow-origin 을 *로 설정해줬어야하나?
근데 서버 디스크는 뭐가 다르길래 되었던 걸까? (찾아봐야함)
'자바스크립트 궁금증' 카테고리의 다른 글
쿠키, 세션스토리지, 로컬스토리지 (0) | 2024.01.15 |
---|---|
자바스크립트 - 동기처리와 비동기처리 (0) | 2024.01.05 |
1220 / 비동기 처리 패턴 promise (0) | 2023.12.28 |
1219 / html 메서드 / fetch (0) | 2023.12.19 |
1216 / 삼항연산자와 구조분해 (0) | 2023.12.16 |