null LiferayポートレットにQRコード読み取り機能を実装する

今回はLiferayポートレットにQRコード読み取り機能を実装する方法をご紹介しようと思います。
具体的には、スマホやタブレット端末でLiferayのポートレットからカメラを起動し
QRコードを読み取って取得した文字列を、INPUTフィールドに反映させるという機能です。

大まかな実装は下記の通りです。
1. カメラを起動するボタンや、カメラ起動時の画面などを実装する
2. JavaScriptでデバイスのカメラにアクセスして、動画を取得する
3. Canvasを使って画像に変換し、画像の中にQRコードが含まれているか解析する



1. カメラを起動するボタンや、カメラ起動時の画面などを実装する

まずはview.jspに、カメラを起動するボタンを配置します。

<%@ include file="/init.jsp" %>
<portlet:renderURL var="readQrcordUrl">
    <portlet:param name="mvcPath" value="/qrcord.jsp" />
</portlet:renderURL>
<aui:input type="text" id="qrcord-string">
<aui:button value="Read QRCord" id="btn-qrcord"cssClass="btn btn-default btn-primary"/>
<aui:script use="aui-base">
    A.one('#<portlet:namespace/>btn-qrcord').on('click', function(){
        window.location.href = "<%=readQrcordUrl%>";
    })
</aui:script>

次にqrcord.jspというファイルを作成して、read qrcordボタンを押下すると遷移する画面を実装します。
videoタグを配置して動画を取得し、非表示のcanvasタグに画像を表示させています。
また、QRコードの解析に利用するjsQRというライブラリと、カメラを起動するapp.jsを読み込んでいます。

<%@ include file="/init.jsp" %>
<div class="reader">
    <video
        id="js-video"
        class="reader-video"
        autoplay
        playsinline
    ></video>
</div>
<div style="display: none">
    <canvas id="js-canvas"></canvas>
</div>

<script src="https://cdn.jsdelivr.net/npm/jsqr@1.4.0/dist/jsQR.min.js"></script>
<script src="<%=request.getContextPath()%>/js/app.js"></script>


2. JavaScriptでデバイスのカメラにアクセスして、動画を取得する

ここからはjsフォルダを作成し、その配下にapp.jsというファイルを作成して実装していきます。
まず、Read QRCordのボタンを押下するとカメラが起動するようにします。
ここでfacingMode: "user"とするとインカメラになるため、PCでも起動することができます。

   const video = document.querySelector('#js-video');
   const initCamera = () => {
        navigator.mediaDevices
            .getUserMedia({
                audio: false,
                video: {
                  facingMode: {
                      exact: 'environment'
                  }
                }
            })
            .then((stream) => {
                video.srcObject = stream;
                video.onloadedmetadata = () => {
                    video.play();
                    checkQRUseLibrary();
                }
            })
            .catch((err) => {
                console.log(err);
            })
    }
      initCamera();


3. Canvasを使って画像に変換し、画像の中にQRコードが含まれているか解析する

videoの出力をcanvasに描画して画像に変換するように、app.jsを実装を加えます。
jsQRで解析が実行され解析できた場合は、文字列をパラメータにセットしたURLを開きます。
このパラメータを、INPUTフィールドへの反映などに利用することができます。
また、解析できなった場合は200ms後に、再度解析を実行させます。
これが大きすぎると読み取りに時間がかかります。逆に小さすぎると解析回数が多くなり負荷がかかる可能性があります。

    const checkQRUseLibrary = () => {
        const canvas = document.querySelector('#js-canvas')
        const ctx = canvas.getContext('2d')
        ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
        const code = jsQR(imageData.data, canvas.width, canvas.height)

        if (code) {
            alert("find string:" + code.data);
            var url = new URL(document.referrer);
            url.searchParams.append('qrcordString', code.data);
            window.location.href = url;
        } else {
            setTimeout(checkQRUseLibrary, 200);
        }
    }

TIPS

実際にスマホやタブレットなどからお試しいただくには、ローカルネットワーク内のLiferayを開く方法が便利です。
Liferayを起動しているPCと同じwifiに接続しているモバイル端末のブラウザから、ローカルIPアドレスにアクセスします。
ローカルIPアドレスは下記の方法で確認できます。

macの場合:システム環境設定→ネットワーク を開くと、wifiの接続状況の下部に表示されています。
windowsの場合:コマンドプロンプトで下記を実行すると確認できます。

netstat -nao | find ":80" 


さらにローカルアクセスでモバイル端末からカメラを起動する場合には、HTTPSでアクセスする必要があります。
tomcatの場合、下記のような設定をすることで、https://localhost:8443にHTTPSでアクセスできるようになります。

// キーストアをTomcatのconfフォルダ内に作成する
cd [%TOMCAT_HOME%]\conf
keytool -genkey -alias tomcat -keyalg RSA -keystore keystore.jks

// Tomcatのconfフォルダ内のserver.xml内のSSL設定のコメントを外す
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
            maxThreads="150" SSLEnabled="true">
    <SSLHostConfig>
        <Certificate certificateKeystoreFile="conf/keystore.jks" type="RSA" />
    </SSLHostConfig>
</Connector>


また、カメラの起動時には、位置情報の取得とカメラの利用を許可する必要がある点にもご注意ください。


まとめ

以上、ポートレットからデバイスのカメラを起動しQRコードを読み取る実装をご紹介いたしました。
それでは、次回の記事もお楽しみに。

RANKING
2021.1.08
2020.12.28
2020.12.01
2020.10.30
2020.12.18