リンク先のサムネイル画像を表示させる(画像保存無し)
リンク先のサムネイル画像を表示させる(画像保存無し):
ログイン不要で自由投稿が可能なものを考えてみたときに、リンクを貼られた場合にどんなサイトかわからないのが嫌でしたので、WEBページのサムネイルを表示できないかと思い、試行錯誤の結果実現できたものです。
(そんなサービスを作る気もないのですが・・・試したくなってしまいましたので)
※サーバーにはサムネイル画像は保存したくない前提です。
まずスクリーンショットが取れるツールということで、以下のサービスがありましたが、規約とかに引っかかると面倒なので却下。
HeartRails Capture | サムネイル画像/PDF ファイル作成サービス
htmlをcanvas画像に変換できる以下のツールがありましたのでこれを使ってみました。
html2canvas
documentation
Ajaxでリンク先のページのhtmlを取得し、それをcanvas化して画面に出力というイメージで作業しました。
これはサムネイルなのでサイトの大体の見た目がわかればよかったのでとりあえずはOK。
canvasに取り込まれている画像がCross-Originなのでcanvasが汚染されて画像に出来ないということらしいです。
どうしようもないので、画像にすることはあきらめ、canvasのまま表示させることにしました。
CORS問題とか色々あって出来そうにないと思っていました。一応できました!しかし、後々気づきました、残念ですが、画面表示維持のためにiframeでallow-scriptsにしているのですが悪意のあるページをリンク表示したら危ない気がしてきました・・・
概要
ログイン不要で自由投稿が可能なものを考えてみたときに、リンクを貼られた場合にどんなサイトかわからないのが嫌でしたので、WEBページのサムネイルを表示できないかと思い、試行錯誤の結果実現できたものです。(そんなサービスを作る気もないのですが・・・試したくなってしまいましたので)
※サーバーにはサムネイル画像は保存したくない前提です。
技術選定
まずスクリーンショットが取れるツールということで、以下のサービスがありましたが、規約とかに引っかかると面倒なので却下。HeartRails Capture | サムネイル画像/PDF ファイル作成サービス
htmlをcanvas画像に変換できる以下のツールがありましたのでこれを使ってみました。
html2canvas
documentation
実装方法
Ajaxでリンク先のページのhtmlを取得し、それをcanvas化して画面に出力というイメージで作業しました。
詰まったところ
- CORS問題
- 取得してきたhtmlの見た目に影響を与えずに画面に配置してサムネイルを取得したい
base
タグで見た目維持を行い、iframe
に出力することで解決。
問題点
- 私が使用したバージョンの
html2canvas
はsvg画像がキャプチャできなかった
これはサムネイルなのでサイトの大体の見た目がわかればよかったのでとりあえずはOK。
- canvasを
toDataURL()
で画像に出来なかった
canvas.toDataURL()
を使用したところDOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.
どうしようもないので、画像にすることはあきらめ、canvasのまま表示させることにしました。
結果
<!doctype html> <html lang="ja"> <head> <meta charset="utf-8"> <title>外部リンクのサムネイルを表示する</title> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script src="https://html2canvas.hertzen.com/dist/html2canvas.min.js"></script> <script> document.addEventListener("DOMContentLoaded", () => { let index = 0; document.querySelectorAll('.link-area').forEach(async (target) => { const link = target.querySelector('a').getAttribute('href'); const response = await axios({ method:'get', url:'/cors?url=' + link, }); // キャプチャ対象のhtmlをレンダリングするiframeを作る(hiddenにしてabsoluteにして不可視にしておく) const renderAreaId = 'render-area' + index; const renderArea = '<iframe id="' + renderAreaId + '" sandbox="allow-scripts allow-same-origin" width="1280" height="1024" scrolling="no" frameborder="no" style="position: absolute;"></iframe>' document.querySelector('#load-area').insertAdjacentHTML('beforeend', renderArea); const iframe = document.querySelector('#' + renderAreaId); iframe.contentDocument.open(); iframe.contentDocument.write(response.data); iframe.contentDocument.close(); iframe.onload = async () => { // キャプチャを取得 const canvas = await html2canvas(iframe.contentDocument.querySelector('body'),{ logging: false, allowTaint: true, useCORS: true, width: 1280, height: 1024, }); canvas.style.width = parseInt(canvas.width / 4, 10) + 'px'; canvas.style.height = parseInt(canvas.height / 4, 10) + 'px'; target.insertBefore(canvas, target.firstChild);// aタグの前にキャプチャを追加 } index++; }); }); </script> <style> .link-area canvas { border: 2px solid #000; margin: 1rem; } </style> </head> <body> <div id="load-area" style="visibility: hidden;"></div> <div class="link-area"> <a href="https://github.com/">https://github.com/</a> </div> <div class="link-area"> <a href="https://www.google.com/">https://www.google.com/</a> </div> <div class="link-area"> <a href="https://qiita.com/">https://qiita.com/</a> </div> </body> </html>
コメント
コメントを投稿