经验分享
JS解析二维码
# 前言在上一篇文章,我们介绍了使用 `qrcodejs` 进行二维码的生成,这篇文章我们来了解一下 `二维码的解析`,对于 `API调用工程师` 来说,学会 `调用` 每一个 `api` 是我们应
2023-04-23 22:24:47
20

前言

在上一篇文章,我们介绍了使用 qrcodejs 进行二维码的生成,这篇文章我们来了解一下 二维码的解析,对于 API调用工程师 来说,学会 调用 每一个 api 是我们应尽的责任和义务。

介绍

解析二维码,我们将用到两个插件,llqrcodejsqr,为什么用两个插件呢,原因在于二维码解析可能会失败,当我们使用第一个插件解析失败之后,使用第二个插件再次解析,极大的提高二维码解析的成功率

本篇文章同样以一个 html 为例,点击按钮选择图片,监听回调这里就不做介绍,主要从选择完图片之后 开始解析 说起。

开始

下载引入插件

首先我们引入这两个插件,打开 npm官网,搜索 llqrcode

image.pngimage.png

点击 code,打开 index.js 文件:

image.pngimage.png

将里面的代码 copy 下来,在你 html 同级新建一个 llqrcode.js 文件,将复制的代码粘贴进去:

image.pngimage.png

接下来下载第二个插件 jsqr, 同样打开 npm官网,搜索 jsqr,打开 /jsqr/dist/jsQR.js 文件

image.pngimage.png

将里面的代码 copy 出来,粘贴在 llqrcode.js 同级的 jsqr.js 文件中

image.pngimage.png

最后在我们的 html 中引入这两个插件:

<script src="./llqrcode.js"></script>
<script src="./jsqr.js"></script>

第一次解析

需要先定义一个方法getObjectURL,用来将 File 转为 blob 格式的临时路径,这段代码兼容了不同内核的浏览器,网上也都能找到,就不再详讲:

getObjectURL(file) {
    let url = null;
    if (window.createObjectURL != undefined) { // basic
        url = window.createObjectURL(file);
    } else if (window.URL != undefined) { // mozilla(firefox)
        url = window.URL.createObjectURL(file);
    } else if (window.webkitURL != undefined) { // webkit or chrome
        url = window.webkitURL.createObjectURL(file);
    }
    return url;
},

定义一个解决解码结果中文乱码的方法:

function decodeStr(str) {
   var out, i, len, c;
   var char2, char3;
   out = "";
   len = str.length;
   i = 0;
   while (i < len) {
       c = str.charCodeAt(i++);
       switch (c >> 4) {
           case 0:
           case 1:
           case 2:
           case 3:
           case 4:
           case 5:
           case 6:
           case 7:
               // 0xxxxxxx
               out += str.charAt(i - 1);
               break;
           case 12:
           case 13:
               // 110x xxxx 10xx xxxx
               char2 = str.charCodeAt(i++);
               out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
               break;
           case 14:
               // 1110 xxxx 10xx xxxx 10xx xxxx
               char2 = str.charCodeAt(i++);
               char3 = str.charCodeAt(i++);
               out += String.fromCharCode(((c & 0x0F) << 12) |
                   ((char2 & 0x3F) << 6) |
                   ((char3 & 0x3F) << 0));
               break;
       }
   }
   return out;
}

假设现在有一个方法 selectFiles,这是我们选择图片完成的回调,选择到的图片在事件参数 e.target.files 中:

function selectFiles(e){
  const files = e.target.files; 
}

循环所有的 files,通过 getObjectURL 生成 临时url,定义一个全局变量 allQr,并把每一个 url 存储进去,再调用 qrcode.decode 来解码所有的二维码,:

function selectFiles(e){
  const files = e.target.files;
  for(let i = 0; i < files.length; i ++){
    const url = getObjectURL(files[i]);
    allQr.push(url);
    qrcode.decode(url,e);
  }
}

然后通过 qrcode.callback 来获取解码之后的结果,需要注意的是,这个回调是异步的,需要从回调参数里的pathallQr 的url进行关联:

qrcode.callback = async function (imgMsg,path) {
    allQr.forEach(async url => {
        if(url == path){
            // 定义解码失败的标识
            const errArr = ['error','Failed'];
            if(errArr.indexOf(imgMsg.split(' ')[0]) == -1){
                // 解码成功之后解决中文乱码
                item.code = decodeStr(imgMsg);
            }else{
                // 解码失败,二次解码
                // ...
            }
        }
    })
}

接下来开始二次解码,使用 promise 封装一个解码方法

base64ToqR(data) {
    return new Promise((resolve,reject) => {
        const c = document.getElementById("qrcanvas");
        const ctx = c.getContext("2d");
        const img = new Image();
        img.src = data;
        img.onload = function() {
            $("#qrcanvas").attr("width",img.width)
            $("#qrcanvas").attr("height",img.height)
            ctx.drawImage(img, 0, 0, img.width, img.height);
            const imageData = ctx.getImageData(0, 0, img.width, img.height);
            const code = jsQR(imageData.data, imageData.width, imageData.height, {
                inversionAttempts: "dontInvert",
            });
            resolve(code);
        };
    })
},

在第一次解码失败的地方,调用二次解码方法:

qrcode.callback = async function (imgMsg,path) {
    allQr.forEach(async url => {
        if(url == path){
            // 定义解码失败的标识
            const errArr = ['error','Failed'];
            if(errArr.indexOf(imgMsg.split(' ')[0]) == -1){
                // 解码成功之后解决中文乱码
                item.code = decodeStr(imgMsg);
            }else{
                // 解码失败,二次解码
                const {data} = await base64ToqR(url);
                if(data){
                    item.code = decodeStr(data);
                }else{
                    item.errmsg = '检测不到二维码或二维码识别率低'
                }
        }
    })

更大程度上保证了解码的成功率。

最后,我是用这两个插件,做了这么一个批量解码的小 demo,欢迎大家体验:批量解码

结语

我是一名前端程序员,但不止于前端,如果你对上述文章有所见解,欢迎评论区讨论,大家一起学习,一起进步~,最后,再次感谢你能看到这里。