[Javascript] canvas 캡쳐 후 다운로드

2024. 11. 12. 14:03기술 창고/Javascript

728x90
반응형
SMALL

이전에 canvas 로 데이터들을 통합하여 만들었었는데, 그 때 만들었던 쿠폰 canvas의 경우 저장하기 기능이 존재했기 때문에 이 canvas로 만들어진 데이터를 캡쳐한 이후에 다운로드 되는 기능이 존재해야 했습니다.

오늘은 이 기능을 javascript로 구현해보는 방법을 정리해보겠습니다.

 

 

HTML

<div class='col-12 couponImg' id="captureCoupon">
    <!-- 노출 canvas 영역 -->
    <canvas id="canvas" style="width: 100%;"></canvas>
</div>

<!-- canvas 저장 버튼 -->
<button type='button' onclick="capture();">
    바코드 저장하기
</button>

하나로 만들어진 canvas 영역과 해당 canvas 데이터를 다운로드 받을 button 을 만들어주었습니다.

 

 

Javascript

// 쿠폰 + 바코드 스크린샷 다운로드
function capture() {
    html2canvas(document.getElementById('canvas'), {
        allowTaint: true,
        useCORS: true
    })
        .then(function (canvas) {
            var captureImgData = canvas.toDataURL('image/png');
            var downloadLink = document.createElement('a');

            downloadLink.href = captureImgData;
            downloadLink.download = 'screenshot.png';
            downloadLink.click();
        });
}

버튼 동작 관련 Javascript 코드를 만들어줍니다.

저는 캡쳐를 하기위해 html2canvas 라이브러리를 import 받아 사용했습니다.

 

(# 저는 https://github.com/niklasvh/html2canvas/releases 사이트에서 latest 버전의 html2canvas.min.js 를 다운받아 프로젝트에 별도로 넣어놓고 import 받아 호출하여 사용했습니다.)

 

html2canvas를 통해 html 에 만들어진 canvas 영역을 호출하고 우선 설정값을 부여해줍니다.

 

- allowTaint : cross origin 허용 / 비허용 설정

- useCORS : CORS 보안을 사용한 서버로부터 이미지 접근 허용 설정

 

그 다음, html2canvas로 호출한 canvas를 image/png 형식의 데이터로 지정 설정합니다.

canvas를 다운로드 하기 위한 새로운 a 태그 구조를 생성하고,  해당 a 태그에 download 속성을 부여하면서, 다운로드 시 저장될 파일명을 설정해줍니다.

그리고 본격적으로 다운로드를 실행할 click 함수를 넣어 바로 다운로드가 실행될 수 있도록 합니다.

 

 

결과

이제 본격적으로 서버에 배포해서 모바일과 데스크탑 환경으로 각각 해당 버튼을 눌러 실행해보면,

데스크탑 환경

 

모바일 환경

 

정상적으로 모든 환경에서 캡쳐 후 다운로드 되고 canvas에 적용된 모든 내용들이 나오는 것을 확인할 수 있습니다.

 

 

+ 추가

그런데 위와 같은 방식을 적용했을 때 99프로 이상의 모바일 환경의 브라우저나 데스크탑의 브라우저 환경에서 정상적으로 동작되는 것을 확인할 수 있으나, 네이버 앱을 통해 실행하게 되면 동작되지 않을 것입니다.

(# 이상한 점은 ios 에서 네이버앱을 통해 실행했을 때는 된다는 것...)

 

이는 canvas의 데이터를 다운로드 하기 위해서는 위의 코드처럼 a 태그의 download, click 함수가 실행되야 하고 href 에 다운로드 받을 데이터의 경로가 들어가야 되는데, 위의 canvas.toDataURL('image/png') 함수를 실행했을 때, 형식은 png 이지만 전체적인 데이터 형식은 base64 기반의 암호화된 데이터로 들어가게 됩니다.

이 base64 기반의 암호화 데이터는 대부분의 플랫폼, 환경에서는 그대로 넣어도 무리없이 정상적으로 캡처 후, 다운로드가 되지만, 네이버 앱에서는 이 base64 기반의 데이터를 막아놓은 것으로 보입니다.

 

따라서, 네이버 앱까지 포괄적으로 정상적으로 캡처 후 다운로드가 되게 하기 위해서는 방식을 바꿀 필요가 있습니다.

 

 

Javascript (변경 후)

// 쿠폰 + 바코드 스크린샷 다운로드
function capture() {
    const canvas = document.getElementById('canvas');
    var captureImgData = canvas.toDataURL('image/png');

    const imgData = atob(captureImgData.split(",")[1]);
    const len = imgData.length;
    const buf = new ArrayBuffer(len); // 비트를 담을 버퍼를 만든다.
    const view = new Uint8Array(buf); // 버퍼를 8bit Unsigned Int로 담는다.

    let blob, i;

    for (i = 0; i < len; i++) {
        view[i] = imgData.charCodeAt(i) & 0xff; // 비트 마스킹을 통해 msb를 보호한다.
    }

    // Blob 객체를 image/png 타입으로 생성한다. (application/octet-stream도 가능)
    blob = new Blob([view], { type: "image/png" });
    const url = window.URL.createObjectURL(blob); // blob:http://localhost:1234/28ff8746-94eb-4dbe-9d6c-2443b581dd30

    var downloadLink = document.createElement('a');
    downloadLink.href = url;
    downloadLink.download = 'capture_coupon.png';
    downloadLink.click();
}

수정한 Javascript 코드 로직을 보겠습니다.

canvas.toDataURL('image/png') 통해 암호화된 base64 기반의 데이터를 확인해보면 이렇습니다.

 

"data:image/png;base64, /9j/4Aadfnbljkndfasdfjlbgnladnfblnsldfnblsndfblxndjfngblsnrtnbu~~~"

 

base64 텍스트 뒤 쪽에 있는 암호화된 데이터가 실제 canvas의 암호화 데이터라고 보면 됩니다.

 

이를 우선 , 기호 기준으로 자르고 뒤 쪽의 실제 암호화 데이터를 디코딩하여 가지고 옵니다.

디코딩된 데이터의 길이를 가지고 ArrayBuffer를 만들고, 이 ArrayBuffer를 가지고 다시 8 bit Unsigned Int 형식의 Uint8Array 를 만들어줍니다.

 

이 Uint8Array에 디코딩된 데이터를 처음부터 한 글자씩 비트 마스킹(보안 처리) 하여 차곡차곡 넣어줍니다.

데이터가 전부 넣어진 Uint8Array를 blob 객체로 변환해주며, type 유형을 image/png 로 만들어줍니다.

 

window.URL.createObjectURL(blob) 을 통해 최종적으로 만들어진 blob 객체의 현재 존재하는 URL 경로를 추출해줍니다.

제 서버에서 이 URL 을 확인해보면, http://{배포 서버 ip 및 포트}/{난수화된 파일명} 형식으로 추출된 것을 확인할 수 있습니다.

 

이제 모든 준비는 끝났습니다.

이 추출한 URL을 가지고 다시 새로운 a 태그를 만들고, href에 추출한 url을 넣은 다음,  download 속성을 부여하여 click() 함수를 호출하도록 합니다.

 

 

결과2

네이버앱 환경 확인
모바일 갤러리 확인

 

정상적으로 캡처 후 다운로드가 실행되면서 파일이 저장되었다는 알림이 나오는 것을 확인할 수 있습니다.

이제 모든 환경에서(아마도?) canvas 데이터를 캡처하여 다운로드 받을 수 있게 되었습니다.

728x90
반응형
LIST