はじめに
実務にて、入力フォームで選択した画像を送信前に QR コード判定して、送信後に URL と 画像ファイルを表示してほしいという小仕事の依頼があり、それを解決した情報になります。ソースコードをコピペ(コピー&ペースト)するだけで簡単に流用ができます。また、ライブラリとプラグインのバージョンにお気を付けください。
ソースコード
<form method="post">
<input type="file" id="inputQRCode" name="inputQRCode" accept=".png,.jpg">
<input type="hidden" id="inputQRCodeString" name="inputQRCodeString">
<input type="submit">
</form>
<div id="error" style="color: red;"></div>
<p>File Name : <?= $_POST['inputQRCode'] ?></p>
<p>URL : <?= $_POST['inputQRCodeString'] ?></p>
<p>QR Code :</p>
<div id="outputQRCode"></div>
<script src="//cdn.jsdelivr.net/npm/jsqr@1.3.1/dist/jsQR.min.js"></script>
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script src="//cdn.jsdelivr.net/npm/jquery.qrcode@1.0.3/jquery.qrcode.min.js"></script>
<script>
const canvas = document.createElement('canvas')
const reader = new FileReader()
inputQRCode.addEventListener('change', e => {
try {
const file = e.target.files[0]
reader.onload = function() {
const image = new Image()
image.onload = function() {
canvas.width = this.width
canvas.height = this.height
const ctx = canvas.getContext('2d')
ctx.drawImage(image, 0, 0)
const imageData = ctx.getImageData(0, 0, this.width, this.height)
const code = jsQR(imageData.data, imageData.width, imageData.height)
if (!code) {
inputQRCode.value = ''
error.textContent = 'QR コードを選択してください。'
return
}
inputQRCodeString.value = code.data
}
const src = reader.result
if (!src) {
inputQRCode.value = ''
error.textContent = '画像の読み込みに失敗しました。'
return
}
image.src = src
}
reader.readAsDataURL(file)
error.innerHTML = ''
} catch (e) {
inputQRCode.value = ''
error.textContent = e.message
}
})
<?php if ($_POST['inputQRCodeString']): ?>
$('#outputQRCode').qrcode({width: 128, height: 128, text: "<?= $_POST['inputQRCodeString'] ?>"})
<?php endif; ?>
</script>
検証環境
- HTML / マニュアル
- JavaScript / マニュアル
- PHP 7.3.8 / マニュアル
- Apache 2.2.34 / マニュアル
- Google Chrome 81.0.4044.129
- Safari 13.1
ライブラリ & プラグイン
解説
QR コード判定
選択ファイルの QR コード判定には JavaScript ライブラリ「 jsQR 」を利用します。ライブラリの読み込みは CDN を利用しました。
<script src="//cdn.jsdelivr.net/npm/jsqr@1.3.1/dist/jsQR.min.js"></script>
input 要素の change イベント発生後、addEventListener() メソッドで QR コード判定処理が走る仕掛けを作ります。
<input type="file" id="inputQRCode" name="inputQRCode" accept=".png,.jpg">
inputQRCode.addEventListener('change', e => {
// 処理
})
例外処理文 を記述します。
try {
// 処理
} catch (e) {
inputQRCode.value = ''
error.textContent = e.message
}
FileReader クラスのインスタンスを new 演算子 で生成、
const reader = new FileReader()
選択ファイルの FileList オブジェクトを変数に格納、FileReader.onload プロパティを使って、ファイルの読み込み完了後に処理が走る仕掛けを作ります。
const file = e.target.files[0]
reader.onload = function() {
// 処理
}
Image() コンストラクタで img 要素を生成、load イベント発生後に処理が走る仕掛けを作ります。
const image = new Image()
image.onload = function() {
// 処理
}
document.createElement() メソッドで canvas 要素を生成して、width 属性と height 属性に値を設定、getContext() メソッドで 2D レンダリングコンテキストを取得、drawImage() メソッドで canvas 要素に画像を描画します。
const canvas = document.createElement('canvas')
canvas.width = this.width
canvas.height = this.height
const ctx = canvas.getContext('2d')
ctx.drawImage(image, 0, 0)
描画した画像データから getImageData() メソッドで ImageData オブジェクトを取得します。
const imageData = ctx.getImageData(0, 0, this.width, this.height)
javascript ライブラリ jsQR の jsQR() メソッドに ImageData オブジェクトを渡してデコード、QR コード情報が詰まったオブジェクトを取得しています。
const code = jsQR(imageData.data, imageData.width, imageData.height)
ImageData オブジェクトのデコードに失敗した場合の処理を記述します。
if (!code) {
inputQRCode.value = ''
error.textContent = 'QR コードを選択してください。'
return
}
QR コード文字列を送信する為に input 要素の value 属性に値を設定します。
<input type="hidden" id="inputQRCodeString" name="inputQRCodeString">
inputQRCodeString.value = code.data
FileReader.result プロパティで データ URL を取得します。
const src = reader.result
データ URL の取得に失敗した場合の処理を記述します。
if (!src) {
inputQRCode.value = ''
error.textContent = '画像の読み込みに失敗しました。'
return
}
生成した img 要素の src 属性に データ URL を設定します。
image.src = src
FileReader.readAsDataURL() メソッドでファイルを読み込みます。
reader.readAsDataURL()
また、特定の URL のみ許可したい場合は、QR コード情報に判定を追加することで実現できます。
const services = [
'http://webgroove.work',
'https://webgroove.work'
]
if (!services.filter(service => code.data.startsWith(service))[0]) {
inputQRCode.value = ''
error.innerHTML = 'Web Groove の QR コードを選択してください。'
return
}
以上です。
QR コード出力
送信後の QR コード出力には jquery プラグイン jquery.qrcode.js を利用しました。ライブラリとプラグインの読み込みは CDN を利用しました。
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
</script><script src="//cdn.jsdelivr.net/npm/jquery.qrcode@1.0.3/jquery.qrcode.min.js"></script>
jquery プラグイン jquery.qrcode.js の qrcode() メソッドに受信データの文字列を渡して QR コードを出力します。今回、受信データは PHP タグ の $_POST から取得しています。
<div id="outputQRCode"></div>
<?php if ($_POST['inputQRCodeString']): ?>
$('#outputQRCode').qrcode({width: 128, height: 128, text: "<?= $_POST['inputQRCodeString'] ?>"})
<?php endif; ?>
以上です。
おわりに
QR コード判定の別解として、Google Cloud Vision API の AI に画像を解析させて実現することも可能です。