zoukankan      html  css  js  c++  java
  • 基于 zxing 的二维码生成、解析

      在很多的场景下我们需要用到二维码,这里就通过google的zxing来对二维码进行实现。

    二维码生成:

    1.导入依赖:

    <dependency>
      <groupId>com.google.zxing</groupId>
      <artifactId>core</artifactId>
      <version>2.2</version>
    </dependency>
    <dependency>
      <groupId>com.google.zxing</groupId>
      <artifactId>javase</artifactId>
      <version>2.2</version>
    </dependency>

    2.编写二维码工具类,用于讲生成的二维码图片通过流的形式写到浏览器,同时支持在二维码中间添加定制图片:

    import com.google.zxing.*;
    import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
    import com.google.zxing.common.BitMatrix;
    import com.google.zxing.common.HybridBinarizer;
    import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
    
    import javax.imageio.ImageIO;
    import javax.servlet.http.HttpServletResponse;
    import java.awt.*;
    import java.awt.geom.RoundRectangle2D;
    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.io.IOException;
    import java.util.Hashtable;
    
    import static org.apache.catalina.manager.Constants.CHARSET;
    
    public class RecodeUtil {
    
        private static final int WIDTH = 30;
        private static final int HEIGHT = 30;
    
    
        public static void creatRrCode(String contents, int width, int height, HttpServletResponse response) throws Exception {
            Hashtable hints = new Hashtable();
    
            hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); //容错级别最高
            hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); //设置字符编码
            hints.put(EncodeHintType.MARGIN, 1); //二维码空白区域,最小为0也有白边,只是很小,最小是6像素左右
            try {
                BitMatrix bitMatrix = new MultiFormatWriter().encode(contents, BarcodeFormat.QR_CODE, width, height, hints); // 1、读取文件转换为字节数组
                BufferedImage image = toBufferedImage(bitMatrix);
                //转换成png格式的IO流
                ImageIO.write(image, "png", response.getOutputStream());
            } catch (WriterException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * image流数据处理
         */
        public static BufferedImage toBufferedImage(BitMatrix matrix) throws Exception {
            int width = matrix.getWidth();
            int height = matrix.getHeight();
            BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            for (int x = 0; x < width; x++) {
                for (int y = 0; y < height; y++) {//0xFF000000  0xFFFFFFFF
                    //https://blog.csdn.net/cgwcgw_/article/details/21155229 颜色查询
                    image.setRGB(x, y, matrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
                }
            }
            insertImage(image,"D:/head.jpg",true,width,height);//调用insertImage函数插入图片
            return image;
        }
        /**
         * 插入内嵌图片
         * @param source
         * @param imgPath 要插入图片路径
         * @param needCompress 要插入图片的像素是否大于60
         * @throws Exception
         */
        private static void insertImage(BufferedImage source, String imgPath,
                                        boolean needCompress,int qrWidth,int qrHeight) throws Exception {
            File file = new File(imgPath);
            if(!file.exists()){
                System.err.print(""+imgPath+"路径不存在!");
                return;
            }
            Image src = ImageIO.read(new File(imgPath));
            int width = src.getWidth(null);//获得原宽度
            int height = src.getHeight(null);//获得源高度
            if(needCompress){//比较要插入的图片的宽度是否大于设定的WIDTH=30像素宽
                if(width>WIDTH){
                    width = WIDTH;
                }
                if(height>HEIGHT){//比较要插入的图片的高度是否大于设定的HEIGTH=30像素宽
                    height = HEIGHT;
                }
                Image image = src.getScaledInstance(width, height, //把image对象的getScaledInstance方法把图片缩小heightXwidth像素大小
                        Image.SCALE_SMOOTH);
                BufferedImage tag = new BufferedImage(width,height,///创建一个透明色的BufferedImage对象
                        BufferedImage.TYPE_INT_RGB);
                Graphics g = tag.getGraphics();//获得画笔
                g.drawImage(image, 0, 0, null);//绘制指定图像中当前可用的image图像,图像的左上角位于该图形上下文坐标(0,0)的 (x, y)
            }
            //开始画内嵌图片
            Graphics2D graph = source.createGraphics();
            //计算绘画坐标
            int x = (qrWidth-width)/2;
            int y = (qrHeight-height)/2;
            graph.drawImage(src, x, y, width, height, null);//内嵌坐标为(x,y)的地方
            Shape shape = new RoundRectangle2D.Float(x,y,width,width,6,6);
            graph.setStroke(new BasicStroke(3f));
            graph.draw(shape);
            graph.dispose();
        }
    
        public static String decode(File file) throws Exception {
            BufferedImage image;
            image = ImageIO.read(file);
            if (image == null) {
                return null;
            }
            BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image);
            BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
            Result result;
            Hashtable hints = new Hashtable();
            hints.put(DecodeHintType.CHARACTER_SET, CHARSET);
            result = new MultiFormatReader().decode(bitmap, hints);
            String resultStr = result.getText();
            return resultStr;
        }
    
    }

    3.提供请求控制器:

    @RestController
    public class QRCodeController {
    
        @GetMapping("/qrcode")
        public void qrcode(HttpServletRequest request, HttpServletResponse response) throws Exception {
            String content = "你是猪";
            long l = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
            content += String.valueOf(l);
            if (StringUtils.isBlank(content)) {
                System.out.println("404");
                return;
            }
            //调用工具类,生成二维码
            RecodeUtil.creatRrCode(content, 180, 180, response);   //180为图片高度和宽度
        }
    
        @PostMapping("/qrcode/parse")
        public void read(MultipartFile file) throws Exception {
            File toFile = null;
            InputStream ins = null;
            ins = file.getInputStream();
            toFile = new File(file.getOriginalFilename());
            inputStreamToFile(ins, toFile);
            ins.close();
    
            RecodeUtil.decode(toFile);
        }
    
        //获取流文件
        private static void inputStreamToFile(InputStream ins, File file) {
            try {
                OutputStream os = new FileOutputStream(file);
                int bytesRead = 0;
                byte[] buffer = new byte[8192];
                while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
                    os.write(buffer, 0, bytesRead);
                }
                os.close();
                ins.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    4.通过以上的代码就完成了后端代码的编写,接下去看一下前端代码。我前端代码基于  Vue 。:

    // ......省略代码
    <button v-on:click="qrcode">二维码</button><br/>
    <img :src = "qrcodeImage" >
    <img src = "http://localhost:8889/qrcode" >
    
    export default {
      name: 'HelloWorld',
      data () {
        return {
          qrcodeImage: ''
        }
      },
      methods: {
        qrcode: function () {
          this.qrcodeImage = null
          this.$axios({
            method: 'get',
            url: '/api/qrcode',
            responseType: 'arraybuffer'
          }).then(function (res) {
            return 'data:image/png;base64,' + btoa(
              new Uint8Array(res.data)
                .reduce((data, byte) => data + String.fromCharCode(byte), '')
            )
          }).then(data => {
            this.qrcodeImage = data
          }).catch(function (err) {
            alert(err)
          })
        }
    }

    5.启动项目,不点击二维码按钮的时候只有一个固定不变的二维码。当点击二维码按钮可以看到一下效果:

      这样子就可以实现我们的二维码。

    二维码解析:

      二维码解析可以通过调用上述 /qrcode/parse 接口,这里采用 postman作为演示,我们先将上面得到的二维码截图保存:

      然后就可以得到二维码的内容了。解析的代码上面也已经给出。

  • 相关阅读:
    三元表达式 列表和字典推导式 函数对象 名称空间 作用域 global和nonlocal 函数装饰器 枚举对象
    函数参数 打散机制 字符串比较 返回值
    函数简介
    三种字符串的介绍 文件的读写
    字符编码
    数据类型及其常用方法 数据类型转换 可变与不可变 值拷贝与深浅拷贝
    流程控制 while和for循环
    变量命名规范 常量 输入和输出 注释 数据类型 运算符 逻辑运算符
    语言分类 编译型和解释型语言分析 环境变量 代码执行的方式 pip介绍 变量
    Python django tests
  • 原文地址:https://www.cnblogs.com/wuzhenzhao/p/13295245.html
Copyright © 2011-2022 走看看