Spring

[html2canvas] spring에서 html2canvas proxy 사용하기/ 지도 캡처

우주유령 2022. 3. 3. 22:24
728x90
반응형

html2canvas에서 지도의 캡처를 찍으려고 했는데, 이상하게 카카오지도 부분만 나오지 않았다.

 

알아보니, html2canvas에서 외부이미지는 CORS정책 때문에 캡쳐가 안된다고 한다. 내 쪽 서버의 origin과 이미지 요청에 대한 응답 서버의 origin이 맞지 않아서 브라우저에서 막기 때문이다.

 

CORS에 대한 내용은 아래에서 확인하자.

https://wouldyou.tistory.com/37

 

[Web] CORS란, CORS오류 해결

웹 생태계에는 다른 출처(origin)로의 리소스 요청을 제한하는 것과 관련된 두 가지 정책이 존재한다. 바로 CORS와 SOP다. Origin, 출처 이러한 url이 있을 때 origin은 protocol + host + port를 말한다. 예시 :..

wouldyou.tistory.com

 

이를 해결하기 위해서는 proxy를 사용해 우회해야 하는데, 이 옵션을 html2canvas에서 지원한다.

 

 

Proxy

Browse different proxies available for supporting CORS content

html2canvas.hertzen.com

들어가보면 아주 친절하게도 node.js버전은 라이브러리가 잘 되어있는데 java버전 라이브러리는 없다^^

php버전도 있다고한다 https://github.com/brcontainer/html2canvas-php-proxy

 

필자는 java를 사용하므로 node.js버전의 html2canvas-proxy라이브러리를 참고하여 controller에서 직접 구현했다.

 

java로 html2canvas proxy 만들기

아래처럼 proxy옵션을 주면 html2canvas에서 내 origin + proxy뒤에 이미지를 요청하는 url과 responseType이 쿼리로 붙어 요청을 한다.

html2canvas(document.body, {proxy : '/html2canvas-proxy'}).then(function(canvas) {
    document.body.appendChild(canvas);
});

당연히, 이 요청을 받을 수 있는 controller가 없어서 404 오류가 난 것을 볼 수 있다.

그렇다면 이 GET요청을 받는 Html2canvasProxyController.java를 만들고 쿼리의 url부분만 따로 떼어내어 재요청한 후 응답받은 이미지를 return해주면 되지 않을까?

 

된다. 실제로 node.js버전의 html2canvas-proxy라이브러리를 보면 같은 방식으로 처리했다.

 

node.js 버전의 html2canvas-proxy는 어떻게 처리했을까?

proxy옵션을 주면 아래와 같이 url과 responseType이 쿼리로 붙는다고 이야기했다.

http://localhost:8080/html2canvas-proxy?url=???&responseType=blob

html2canvas-proxy 코드를 보면, responseType에는 blob과 text가 있는 듯 하다.

https://github.com/niklasvh/html2canvas-proxy-nodejs/blob/master/server.js

(코드를 보면 알 수 있다.)

 

blob인 경우에는 먼저 url로 새로 요청을 하여 응답을 받는다.

그리고 받은 응답을 그대로 내려준다. ( java에서는 byte[]로 내려준다.)

req.pipe(request(req.query.url).on('error', next)).pipe(res);

text인 경우에는 url로 새로 요청을 하여 받은 응답을 아래와 같은 String으로 내려준다.

 res.send(
            `data:${response.headers['content-type']};base64,${Buffer.from(
                body,
                'binary'
            ).toString('base64')}`
        );

이 방식을 카피하여 java버전을 만들어보자.

 

사실 필자는 map만 잘 보여지면 되어서 reponseType=text는 처리하지 않았다. 이 부분은 여러분이 만들어서 꼭 공유해주시면 좋겠다 ㅎㅎㅎ

 

Controller에서 proxy처리하기

먼저 javascript코드는 proxy옵션을 주어 만들어둔다.

html2canvas(kakaoDiv, {proxy: NS.ctx+'/html2canvas/proxy.json', allowTaint:false})
	.then(canvas => {
		const a = document.createElement('a');
		a.href = canvas.toDataURL('image/png')
        		.replace('image/png', 'image/octet-stream');
		a.download = 'map.png';
		a.click();
	});

 

이제 proxy옵션의 GET요청을 받는 Html2canvasProxyController.java를 만들자.

blob인 경우만 처리했다.

 

1. req에서 url부분을 따로 떼어내어, 새로 GET요청을 보낸다.

2. 받응 응답을 byte로 변환하여 반환한다.

@Controller
@RequestMapping(value="/html2canvas")
public class Html2CanvasProxy {
	
	@RequestMapping(value="/proxy.json", method=RequestMethod.GET)
	@ResponseBody
	public byte[] html2canvasProxy(HttpServletRequest req) {
		byte[] data = null;
		try {
			URL url = new URL(URLDecoder.decode(req.getParameter("url"), 
            			java.nio.charset.StandardCharsets.UTF_8.toString()));
			
			HttpURLConnection connection = (HttpURLConnection) 
            					url.openConnection();
			connection.setRequestMethod("GET");
			
			if(connection.getResponseCode() == 200) {
				data = IOUtils.toByteArray(connection.getInputStream());
			} else {
				System.out.println("responseCode : " 
                			+ connection.getResponseCode());
			}
		} catch (MalformedURLException e) {
			data = "wrong URL".getBytes(java.nio.charset.StandardCharsets.UTF_8);
		} catch(Exception e) {
			System.out.println(e);
		}
		return data;
	}
}

 

이제 지도를 잘 다운받는 것을 볼 수 있다!

 

해당 코드는 아래 github에서 다운받을 수 있다

https://github.com/yjs000/captureHTML

 

GitHub - yjs000/captureHTML: html2canvas와 canvg를 이용해 element를 캡쳐

html2canvas와 canvg를 이용해 element를 캡쳐. Contribute to yjs000/captureHTML development by creating an account on GitHub.

github.com

 

728x90
반응형