上头最近新提的需求,要求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: truevideo: {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 consolethat.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>)运行即可,不会再报错!