纯HTML5+js 实现二维码扫描及拍照上传

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

评论 108
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值