관련 지식
javascript, jquery, file-download, xmlhttprequest, xhr, progress, canvas
이번 내용은 파일 다운로드시에 프로그레시브 바, 즉 파일 다운로드 진행률을 어떻게 보일수 있는지 설명합니다. 하지만 파일 업로드와는 다르게 다운로드는 브라우저에서 진행률을 보여줄수가 있죠. 따라서 진행률을 보이는 것보다는 파일 다운로드를 이렇게도 할 수 있다는것과 파일을 다운로드 완료 시점을 알아낼수 있다는 것에 주목하면 좋을것 같습니다.
진행률 보이기
파일 다운로드 진행률은 canvas
를 이용하겠습니다. 이 코드는 아래와 같은 이미지를 보여줄 것입니다.
<canvas id="canvas" style="display:none"></canvas>
function showPer(per) {
ctx.clearRect(0, 0, 400, 400);
//바깥쪽 써클 그리기
ctx.strokeStyle = "#f66";
ctx.lineWidth = 10;
ctx.beginPath();
ctx.arc(60, 60, 50, 0, Math.PI * 2 * per / 100);
ctx.stroke();
//숫자 올리기
ctx.font = '32px serif';
ctx.fillStyle = "#000";
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(per + '%', 60, 60);
}
써클을 그리기 위해선 arc()
함수를 사용해야 합니다. 원을 그리기 위한 각도가 흔히 아는 각을 쓰지 않고 라디안 값을 사용하기 때문에 MDN의 설명을 한번 읽어보시는게 좋습니다
https://developer.mozilla.org/ko/docs/Web/HTML/Canvas/Tutorial/Drawing_shapes#%ED%98%B8(arc)
파일 다운로드
다운로드와 관련된 여러 외부 플러그인이나 라이브러리가 있지만 iframe
을 이용한 파일 다운로드 방식이 많습니다. 그러나 iframe
을 이용한 방식은 다운로드 완료 시점을 알 수가 없기 때문에 ajax를 이용하여 직접 만들어보겠습니다.
ajax 호출은 평소에 사용하던 형태와 크게 다르진 않지만 xhrFields
속성을 반드시 재정의 해야 합니다. ajax
는 response 데이터를 기본적으로 문자로 처리하기 때문에 css
, json
, html
같이 텍스트 파일이 아닌것을 받으려고 할 경우 잘못된 데이터를 받게 됩니다. 따라서 xhrFields
속성을 지정하여 문자가 아니라 바이너리로 처리하게 해야 합니다.
var url = '/img/bigsize.jpg';
$("#btnSubmit").on("click", function(e) {
$.ajax({
url: url,
type : 'get',
xhrFields: { //response 데이터를 바이너리로 처리한다.
responseType: 'blob'
},
success : function(data) {
console.log("완료");
var blob = new Blob([data]);
//파일저장
if (navigator.msSaveBlob) {
return navigator.msSaveBlob(blob, url);
}
else {
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = url;
link.click();
}
}
});
});
파일을 저장하는 부분은 이전에 올린 ‘html2canvas 를 이용한 웹 화면 캡쳐’ 에서 사용했던 방법을 이용하였습니다.
다운로드 진행률 알아내기
하지만 위 내용으로는 다운로드는 가능하지만 다운로드 진행률은 알아낼 수 없습니다. AJAX에서 사용할 XMLHttpRequest
에 progress 에 대한 이벤트 리스너를 추가하면 다운로드 진행 과정을 전달 받을 수 있습니다.
xhr: function() { //XMLHttpRequest 재정의 가능
var xhr = $.ajaxSettings.xhr();
xhr.onprogress = function(e) {
//퍼센트가 100이 되었을때 다운로드 완료
showPer(Math.floor(e.loaded / e.total * 100));
};
return xhr;
},
끝났습니다. 아주 쉬워요
정리
이 다운로드 방식은 매우 독특합니다. 일반적으로 리소스의 링크를 클릭하여 다운로드를 하는 경우 아래의 흐름대로 동작합니다.
- 다운로드 링크 클릭
- 브라우저에서 서버로 파일 다운로드 요청
- PC의 저장될 경로 선택
- 파일 다운로드(시간 오래 걸림)
- 저장 완료
그러나 이번에 만든 방식은 아래처럼 동작합니다.
- 다운로드 링크 클릭
- 브라우저에서 서버로 파일 다운로드 요청
- 브라우저에 파일 다운로드(시간 오래 걸림)
- PC의 저장될 경로 선택
- 저장 완료
AJAX로 파일을 다운로드 할 경우엔 저장될 경로 선택창이 뜨기 전까지가 느립니다. 따라서 진행률 표시 같은것이 없다면 다운로드 링크 클릭후 아무 반응이 없다고 느낄수 있습니다.
제가 100MB 파일까지 다운로드 테스트 했을땐 이상 없었지만 그 이상의 대용량 파일에서 문제가 있을지는 모르겠습니다. 용량이 작을땐 문제가 없으므로 파일 다운로드 완료 시점을 알아야 하는 특수한 상황에선 안심하고 사용하셔도 될것 같습니다.
최종 소스
<!DOCTYPE html>
<html lang="en">
<head>
<title>CSS Template</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
</head>
<body>
<canvas id="canvas" style="display:none"></canvas>
<input type="button" id="btnSubmit" value="다운로드"/>
<script>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
function showPer(per) {
ctx.clearRect(0, 0, 400, 400);
//바깥쪽 써클 그리기
ctx.strokeStyle = "#f66";
ctx.lineWidth=10;
ctx.beginPath();
ctx.arc(60, 60, 50, 0, Math.PI * 2 * per / 100);
ctx.stroke();
//숫자 올리기
ctx.font = '32px serif';
ctx.fillStyle = "#000";
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(per + '%', 60, 60);
}
var url = '/img/bigsize.dat';
$("#btnSubmit").on("click", function(e) {
$.ajax({
url: url,
type : 'get',
xhrFields: { //response 데이터를 바이너리로 처리한다.
responseType: 'blob'
},
beforeSend : function() { //ajax 호출전 progress 초기화
showPer(0);
canvas.style.display = 'block';
},
xhr: function() { //XMLHttpRequest 재정의 가능
var xhr = $.ajaxSettings.xhr();
xhr.onprogress = function(e) {
showPer(Math.floor(e.loaded / e.total * 100));
};
return xhr;
},
success : function(data) {
console.log("완료");
var blob = new Blob([data]);
//파일저장
if (navigator.msSaveBlob) {
return navigator.msSaveBlob(blob, url);
}
else {
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = url;
link.click();
}
},
complete : function() {
canvas.style.display = 'none';
}
});
});
</script>
</body>
</html>
'javascript' 카테고리의 다른 글
[javascript] moment.js 사용하기 (0) | 2019.05.21 |
---|---|
[javascript] scope와 호이스팅(hoisting) (0) | 2019.05.08 |
[javascript] canvas를 이용한 시계 만들기 (0) | 2019.05.06 |
[javascript] createImageBitmap() 함수 대체법 (0) | 2019.05.03 |
[javascript] canvas 기초 사용법 (0) | 2019.05.02 |