上头最近新提的需求,要求web端实现扫描识别二维码自动跳转页面,并且其他二维码扫描工具扫描出来的结果,与本站点扫描出来的结果不一样
首先分析需求,这种功能一般用于移动设备,PC用的比较少。与其他工具扫描结果不一样,则需要在获取扫描结果后,做相应的操作。
思路:1.生成二维码时,往数据库加入短地址(随机生成,可以是6位大小写英文)与长地址(加密,并生成生成一串MD5附加在后面)
2.获取二维码扫描结果后,首先对二维码中的短地址进行查询,找到对应的长地址,对长地址进行一系列的解密操作,解密过后得到对应的解密数据,并再次生成MD5,与长地址中的MD5进行对比,一致则进入下一步操作,不一致则说明长地址被篡改。验证当前登录用户与生成二维码的用户(长地址中获取)是否一致,必须一致才可以跳转(因为我这边扫描二维码得到的都是私人信息)否则私人信息可能被盗取。
这里就贴上调用摄像头的代码,至于加密解密,可自行百度
HTML代码
<a id="scan" href="javascript:void(0)">二维码扫描</a>
<div hidden="hidden">
<div id="divQRScan">
<div class="tab-content">
<!-- 由于llqrcode.js中写死了id,所以id必须为qr-canvas -->
<video id="QRvideo" muted autoplay playsinline width="400" height="500"></video>
<canvas id="qr-canvas" style="display:none;"></canvas>
</div>
</div>
</div>
<script src="@Url.Content("~/Scripts/QRCodeScan/llqrcode.js")"></script>
<script src="@Url.Content("~/Scripts/QRCodeScan/QRScan.js")"></script>
javascript代码(QRScan.js)
const Scan = {
videoInputDevice: [], //设备列表
videoElement: document.getElementById("QRvideo"),
canvasElement: document.getElementById("qr-canvas"),
decodeTimer: null,
canvasTimer: null,
canvasContext: document.getElementById("qr-canvas").getContext("2d"),
// 获取到的媒体设备
gotDevices (deviceInfos) {
let that = this;
for (let i = 0; i !== deviceInfos.length; ++i) {
let deviceInfo = deviceInfos[i];
if (deviceInfo.kind === 'audioinput') {
// 音频设备
} else if (deviceInfo.kind === 'videoinput') {
// 视频设备
that.videoInputDevice.push(deviceInfo);
} else {
// 其他设备
console.log('Found one other kind of source/device: ', deviceInfo);
}
}
},
getStream () {
let that = this;
if (window.stream) {
window.stream.getTracks().forEach((track) => {
track.stop();
});
}
// if(that.isIOS){
// let constraints = {
// video: { facingMode: { exact: "environment" } }
// };
// console.log('3: ', constraints);
// // let constraints = {
// // video: {
// // // environment表示后置摄像头
// // // user表示前置摄像头
// // facingMode: ("environment")
// // }
// // };
// }else{
let constraints = {
// 包含audio 可声明音频设备调用
// 声明视频设备调用
// video: true
video: {
deviceId: {
// [1].deviceId 表示后置摄像头,默认开启的是前置摄像头
exact: that.videoInputDevice[1].deviceId
}
}
};
// }
// 视频设备初始化
navigator.mediaDevices.getUserMedia(constraints).then(that.gotStream.bind(that)).catch(that.handleError.bind(that));
that.captureToCanvas();
that.decode();
},
// 解码
decode () {
let that = this;
try {
qrcode.decode();
} catch (e) {
console.log('1:' + e);
};
that.decodeTimer = setTimeout(that.decode.bind(that), 100); // 解码频率为100毫秒一次
},
//将视频流放到画布
captureToCanvas () {
let that = this;
try {
// 根据视频大小设置canvas大小
let w = that.videoElement.videoWidth;
let h = that.videoElement.videoHeight;
that.canvasElement.width = w;
that.canvasElement.height = h;
that.canvasContext.drawImage(that.videoElement, 0, 0, w, h);
} catch (e) {
console.log(e);
};
// 100毫秒绘制一次
that.canvasTimer = setTimeout(that.captureToCanvas.bind(that), 100);
},
handleError (error) {
$.colorbox.close();
if (/Android|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent)) {
if (error.toString().indexOf("TypeError: Cannot read property 'deviceId' of undefined") != -1) {
alert("无法访问摄像头!请授予手机浏览器摄像头权限或切换至有摄像头权限的浏览器!");
}
} else {
console.log(error);
}
return;
},
gotStream (stream) {
let that = this;
window.stream = stream; // make stream available to console
that.videoElement.srcObject = stream;
},
isIOS() {
var u = navigator.userAgent;
var IOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端
if (IOS) {
return true;
} else {
return false;
}
},
init () {
let that = this;
// API参考
// https://developer.mozilla.org/zh-CN/docs/Web/API/MediaDevices/enumerateDevices
// 先获取设备列表,方便调用后置摄像头
let devices = navigator.mediaDevices.enumerateDevices().then(that.gotDevices.bind(that));
document.querySelector('#scan').addEventListener('click', () => {
$.colorbox({
scrolling: true,
overlayClose: true,
reposition: false,
innerWidth: "90%",
innerHeight: "90%",
maxWidth: "630px",
maxHeight: "540px",
inline: true,
href: "#divQRScan"
});
that.videoElement.style.display = 'block';
that.videoElement.play();
devices.then(that.getStream.bind(that)).catch(that.handleError.bind(that));
that.canvasContext.clearRect(0, 0, 300, 200);
var hasRun = false; //避免重复执行回调函数
//结果回调
qrcode.callback = (e) => {
if (!hasRun) {
// 清除画布,停止摄像头
clearTimeout(that.decodeTimer);
clearTimeout(that.canvasTimer);
that.canvasContext.clearRect(0, 0, 300, 200);
if (window.stream) {
window.stream.getTracks().forEach((track) => {
track.stop();
});
}
that.videoElement.style.display = 'none';
that.canvasElement.style.display = 'none';
e = e.replace("httq://", "http://").replace("httqs://", "https://"); //这里有时会把http识别为httq,可在这一步替换
$.colorbox.close();
if (e.indexOf("http://") != -1 || e.indexOf("https://") != -1) {
hasRun = true;
//这里可跳转到后台进行解密处理
window.location.href = e;
} else {
hasRun = false;
alert("扫描错误,请重试!");
}
}
}
});
}
};
Scan.init();
llqrcode.js可在百度下载,也可评论邮箱索要,代码直接复制粘贴后稍微修改便可使用,用到了colorbox(此插件百度jquery colorbox下载即可)
拍照上传大同小异,不需要llqrcode.js,只是对canvs操作(canvasContext就是扫描获得的图像),网上也有不少拍照上传的插件,如有需要请至蓝奏云下载(仅提供llqrcode.js):https://wws.lanzoux.com/i6VpCg3kb3i
请大家不要再求源码了!!新建一个HTML页面,把上面的代码复制进去,相关的插件(colorbox,llqrcode.js)引用好(<script src=""></script>)运行即可,不会再报错!