利益相关,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出来的知识点,网上的例子比较分散也不太完整,想一想仍是本身记录一波~
有问题的话望懂行的大佬不吝赐教,道阻且长呀~