JavaScript 识别二维码内容

7,617 阅读2分钟

场景: 最近在做一个特定的项目中(Content-Security-Policy)只能通过图片中获取动态信息,然后搜遍了百度,试了好几种方法,并整理了出来方便后人采坑

吐槽一下,百度搜到10篇有9篇都是复制粘贴的,毫无参考意义,想不懂为什么会有这样的操作,每次搜到一样的都想骂人

获取图片数据流信息

做法是让后台生成图片的时候把信息写入到图片里面,然后前端解析这张图片去获取

/**
 * 获取图片数据(二进制数据流)
 * @param {string} src 请求图片路径
 * @param {(code: string) => void} callback 回调函数
 */
function getImageData(src, callback) {
    const XHR = new XMLHttpRequest();
    XHR.open('GET', src, true);
    XHR.responseType = 'arraybuffer';
    XHR.overrideMimeType('text/plain; charset=x-user-defined');
    XHR.onreadystatechange = function () {
        if (XHR.readyState === 4 && XHR.status === 200) {
            const file = XHR.response || XHR.responseText;
            const blob = new Blob([file], { type: 'image/jpeg' });
            const fr = new FileReader();
            // console.log('Blob', blob);
            fr.readAsText(blob);
            fr.onload = function (e) {
                /** @type {string} */
                const code = e.target.result;
                // console.log(code);
                if (typeof callback === 'function') callback(code);
            }
        }
    }
    XHR.send();
}

这样会有一个问题,就是在 Content-Security-Policy 任何 http 请求都会给拦截,所以这个方法不通过,github 上有个叫 exif-js 的库,具体实现也是 http 请求修改响应类型实现的。

最后想到可以用二维码的方式,然后提取二维码里面的内容去实现 最后无意间发现有一个叫 qrcode 的东西(注意这个跟 github 上搜到的 qrcode.js 不是同一个东西),完美的解决了这个问题。步骤是先用 img 加载一张图片(图片加载不会拦截)然后再用 canvas 转出到 base64,最后解码读取。

实现代码

// 引入js
// <script src="qrcode.js"></script>

/**
 * 获取图片base64
 * @param {string} url 图片路径
 * @param {(base64: string) => void} callback 回调
 * @param {'jpg'|'png'} type 图片格式(可选)
 */
 function getBase64(url, callback, type = 'jpg') {
    const doc = document;
    const canvas = doc.createElement("canvas");
    const ctx = canvas.getContext("2d");
    const img = new Image();
    img.crossOrigin = 'Anonymous';
    img.src = url;
    img.onload = function () {
        canvas.width = img.width;
        canvas.height = img.height;
        ctx.drawImage(img, 0, 0);
        const base64 = canvas.toDataURL('image/' + type);
        if (typeof callback === 'function') callback(base64);
    }
    // doc.body.innerHTML = null;
    // doc.body.appendChild(canvas);
}

const url = document.querySelector('.qrcode').src;
        
getBase64(url, base64 => {
    qrcode.decode(base64);
    qrcode.callback = function (res) {
        // 这里二维码信息是加密过的,所以要解一下
        console.log('二维码信息:', JSON.parse(atob(res)));
        
    }
});

这样就完美绕开了 Content-Security-Policy 的拦截,因为图片加载不受限,而且这个库是纯算法读取二维码信息的,所以不需要 http 请求数据流。我稍稍对这个库做了一些调整,源码出处还不知道,如果知道的可以告诉我。

代码地址