利益相關,POS機驗證二維碼有效性javascript
調試時須要登入設備中查看從二維碼讀頭接收到的數據,比較麻煩,爲了方便平常工做中對二維碼有效性的校驗,想到用電腦的攝像頭識別二維碼進行解析,這裏有兩種方案可供選擇:html
PC客戶端,跨平臺可用Qt,無明顯優勢,缺點是涉及到驗碼,須要開發維護多平臺的SDK,目前SDK支持linux,windows,沒有mac版本。前端
B/S架構,web調用攝像頭,前端讀取二維碼數據,後端驗碼,優勢是使用方便,用瀏覽器就能夠進行解析和驗碼,缺點是在開發完成後發現的,缺點也很明顯,Chrome版本47以後的安全策略禁止http調用攝像頭,麥克風等,須要部署https服務,而且其餘瀏覽器也有相似的限制。java
我本身是linux C開發,偏嵌入式,前端只是在大學的時候寫過簡單的頁面,後面就一直沒碰了,此次就想趁着作這個工具的功夫撿起來看看,因此選擇了方案二。node
爲了方便操做,這裏沒有選擇用上傳圖片的方式進行驗碼,採用的是經過H5頁面調用攝像頭,截取二維碼圖片,調用js實現二維碼圖片解析成碼數據,傳給後端進行驗證,將驗碼結果進行展現,因此這裏至關於有三個步驟linux
直接從網上copy攝像頭調用的代碼,以下:web
<video id="video" autoplay="autoplay" style="background: #ccc;" style="float: left" width="640px" height="480px"></video>
<canvas id="canvas" style="display:none;background-color:#F00;" width="640px" height="480px"></canvas>
<script type="text/javascript"> var video = document.getElementById("video"); var aCanvas = document.getElementById('canvas'); var context = aCanvas.getContext("2d"); var errocb = function (code) { alert(code); }; if (navigator.getUserMedia) { // 標準的API navigator.getUserMedia({ "video": true }, function (stream) { video.srcObject = stream; video.play(); }, errocb); } else if (navigator.webkitGetUserMedia) { // WebKit 核心的API console.log(navigator.webkitGetUserMedia); navigator.webkitGetUserMedia({ "video": true }, function (stream) { video.srcObject = window.webkitURL.createObjectURL(stream); video.play(); }, errocb); } </script>
複製代碼
保存代碼,在瀏覽器本地訪問會發現你的頭出如今了頁面中的窗口裏,這裏基本上就完成了攝像頭的調用。express
因爲是用的攝像頭採集二維碼信息,因此須要定時截取視頻流中的圖像幀進行解析,用canvas drawImage()
方法實現,調用reqrcode.js
的qrcode.decode()
方法進行解析,qrcode.callback()
設置回調函數處理解析後的數據,具體的代碼以下:django
function CatchCode(){
if(canvas != null){
context.drawImage(video, 0, 0, 640, 480);
img.src = canvas.toDataURL("image/png");
try{
qrcode.decode(img.src);
qrcode.callback = function (imgMsg) {
console.log(imgMsg);
if(imgMsg == "error decoding QR Code"){
//alert("請抓穩扶好");
return;
}
var data = StringToBytes(imgMsg);
var hex_string = BytesToHexString(data);
console.log("二維碼解析:" + hex_string);
//alert("二維碼解析:" + hex_string);
$("#QRCode").html(hex_string);
request(hex_string);
}
}catch(m){
alert(m);
}
}
}
複製代碼
因爲項目中所用的二維碼數據是二進制數據,用常規網上使用的二維碼解析工具不能正常解析出碼數據,因此這裏須要對decode
出的二維碼數據進行轉碼處理。flask
function StringToBytes(str) {
var ch, st, re = [];
for (var i = 0; i < str.length; i++ ) {
ch = str.charCodeAt(i); // get char
st = []; // set up "stack"
do {
st.push( ch & 0xFF ); // push byte to stack
ch = ch >> 8; // shift value down by 1 byte
} while ( ch );
// add stack contents to result
// done because chars have "wrong" endianness
re = re.concat( st.reverse() );
}
// return an array of bytes
return re;
}
function BytesToHexString(bytes) {
return bytes.map(function(byte) {
return ("00" + (byte & 0xFF).toString(16)).slice(-2)
}).join('')
}
複製代碼
這樣經過攝像頭掃描二維碼就能夠獲得二維碼數據了,基本上到這裏目的就已經達到了,至少之後再要獲取碼數據的時候,不須要我再開設備,進系統,掃碼,看日誌獲得碼數據,直接用電腦攝像頭獲取到碼數據而後用本地寫好的工具進行驗碼便可。
可是作工具的目的是給別人使用,若是仍是須要開發參與,那就仍是個半成品。因此我又撘了個後端接收前端的二維碼數據。。。
nodejs
後端驗碼Q:爲何選擇nodejs?
其實這裏也沒有啥特別的理由,大佬告訴我用nodejs整個後臺,完美知足我對前端的窺探hahah。。我用的是nodejs的
express
web框架,其實用django
,flask
也是能夠的,奈何java還沒整清楚且開發人員衆多,這裏就不提了
Q:SDK是用C寫的,nodejs調用動態庫如何和C進行交互?
我簡單在網上查了下,nodejs調用動態庫能夠採用
node-ffi
模塊,可是驗碼的SDK使用的結構體有點複雜,我就沒有采起這種方式,用了個最偷懶的辦法,我在linux下有個工具是能夠直接將碼數據做爲參數進行驗碼的,並且日誌打印也比較全,因此我用了nodejs調用本地文件的方式,將接收到的二維碼數據做爲參數執行,將驗證返回的結果,也就是日誌信息,直接返回給前端進行展現,試過以後發現是OK的,因而就採用這種方式,之後有時間能夠試試node-ffi
模塊實踐下
關於nodejs執行系統命令能夠參考下掘金裏的這篇文章,個人工具驗碼返回的日誌量比較大,因此我用的是spawn
方式執行的腳本,效果還不錯,代碼以下:
var express = require('express');
var url = require('url');
var router = express.Router();
var BufferHelper = require('bufferhelper');
const {spawn} = require('child_process');
router.get('/', function(req, res, next) {
var bufferHelper = new BufferHelper();
res.writeHead(200, {'Content-Type': 'text/plain'});
var params = url.parse(req.url, true).query;
console.log("data: " + params.data);
console.log("CA: " + params.rootCA);
const spawnObj = spawn('./tool', [params.data,params.rootCA], {encoding: 'utf-8'});
spawnObj.stdout.on('data', function(chunk) {
bufferHelper.concat(chunk);
});
spawnObj.stdout.on('end',function(){
var html = bufferHelper.toBuffer().toString();
console.log(html);
res.end(html);
})
});
複製代碼
完成開發以後,上傳到服務器,訪問發現攝像頭一直沒有反應,在網上查了一下,發現是Chrome瀏覽器的安全策略致使的,要能正常使用,須要部署https服務,本身生成證書的話,別人用還得導入證書,有些麻煩。因此最終,我作了一個只能我本身使用的工具。。。不過也算是知足了我對於前端的窺探,中間不少內容都是靠Google出來的知識點,網上的例子比較分散也不太完整,想一想仍是本身記錄一波~
有問題的話望懂行的大佬不吝賜教,道阻且長呀~