봄날은 갔다. 이제 그 정신으로 공부하자

[JavaScript] host가 다른 경로의 파일 다운로드하기 본문

카테고리 없음

[JavaScript] host가 다른 경로의 파일 다운로드하기

길재의 그 정신으로 공부하자 2024. 12. 26. 10:17
파일을 다운로드할 때 일반적으로 아래와 같이 만듭니다.
<a href=‘FILE_DOWNLOAD_PATH' download>다운로드</a>
 
하지만 다운로드 받으려는 파일의 host가 현재 사이트와 다른 경우, 일부 파일은 다운로드 되지만 브라우저가 지원하는 타입의 파일은 브라우저에서 직접 보여주는 문제가 발생합니다.
이글은 이러한 문제를 해결하기 위한 방법에 대해 설명합니다.

 

Blob(Binary Large Object) 형태로 다운로드 받기

파일을 다운로드 받기 위해서는 아래와 같이 fetch()함수를 사용해 blob 형태로 다운로드 받아야 합니다.
자세한 설명은 코드 내에 주석으로 설명합니다.
fetch(
	FILE_DOWNLOAD_PATH, // 파일 다운로드 경로
	{method: "GET"}
    )
.then((res) => {
	// fetch() 함수 호출시 바로 호출되는 함수로 blob를 반환 
	// 파일 사이즈가 클 경우, blob를 가져오는데 오래 걸림.			
    return res.blob()
	})
.then((blob) => {
	// 서버에서 blob 객체를 모두 다운로드 받았을 때 호출
	// blob로 a element를 만들어 추가한 후 click() 이벤트를 강제로 발생 시켜 다운로드 되도록 처리
    const url = window.URL.createObjectURL(blob)
    const a = document.createElement('a')
    a.href = url
    a.download = FILE_NAME
    document.body.appendChild(a)
    a.click()
    a.remove()
    window.URL.revokeObjectURL(url)
	})  
.catch((err) => {
	// 실패 시 호출
	});
 
다운로드 진행률 표시
다운로드 받으려는 파일 사이즈가 큰 경우 사용자는 언제 다운로드가 완료될지 모르는 상태로 하염없이 기다려야 하므로
편의성을 위해 아래와 같이 함수를 조금 수정하여 진행률 표시 부분을 추가해줍니다.
자세한 설명은 코드 내에 주석으로 설명합니다.
fetch(
	FILE_DOWNLOAD_PATH, // 파일 다운로드 경로
	{method: "GET"}
    )
.then((res) => {
	// fetch() 함수 호출시 바로 호출
	// ReadableStream 인스턴스에서 read()함수를 재귀호출하여 다운로드 진행률 계산
	// blob대신 response를 반환
    const contentLength = res.headers.get("content-length");
    let loaded = 0;

    return new Response(
    		new ReadableStream({
            	start(controller) {
                	const reader = res.body.getReader();
                    read()
                    function read() {
                    	reader.read()
                        .then((progressEvent) => {
						// 다운로드 완료되었는지 체크
                        	if (progressEvent.done === true) {
                            	controller.close();
                                return;
                            }
                            // 다운로드 진행률 계산
                            loaded += progressEvent.value.byteLength;
                            let downloadedPercent = Math.round((loaded / contentLength) * 100) + "%";
                            controller.enqueue(progressEvent.value);
					
                            read(); 
                        })
                     }
                 }
             })
         )
    })
.then((async response => {
	// blob를 가져올 때 await를 사용해야 하므로 async추가
	// blob가 리턴되지 않으므로 response에서 blob 획득
	// 파일 사이즈가 클 경우, blob를 가져오는데 오래 걸리므로 await를 사용하여 다 가져올 때까지 대기	
	const blob = await response.blob();

	// 나머지는 이전과 동일
    const url = window.URL.createObjectURL(blob)
    const a = document.createElement('a')
    a.href = url
    a.download = FILE_NAME
    document.body.appendChild(a)
    a.click()
    a.remove()
    window.URL.revokeObjectURL(url)
    })  
.catch((err) => {
	// 실패 시 호출
    });
Comments