SpringBoot+zxing+Vue realizes front-end request and back-end QR code image

Scenes

ZXing is an open source, 1D/2D barcode image processing library in multiple formats implemented in Java.

github address:

https://github.com/zxing/zxing

If you follow the microservice version to teach you how to build an environment locally and run the front-end and back-end projects:

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/109363303

On the basis of the SpringBoot+Vue separated front-end and back-end project built above, it is necessary to realize that the front-end clicks the button to request the back-end,

The backend will generate a QR code for the required content, such as the download address of the latest version of the app, and then return it to the front end to display the effect on the front end.

Note:

Blog:
https://blog.csdn.net/badao_liumang_qizhi
Follow the public
account Domineering
programmers Get programming-related e-books, tutorial pushes and free downloads.

achieve

SpringBoot background

It is preferred to introduce the dependency of xing in the pom.xml of the project

        <!-- zxing生成二维码 -->
        <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>core</artifactId>
            <version>3.3.3</version>
        </dependency>

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

Then click a new QRCodeUtil tool to generate a QR code

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 lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.imageio.ImageIO;
import javax.swing.filechooser.FileSystemView;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.util.*;

@Component
@Slf4j
public class QRCodeUtil {

    private static final Logger log = LoggerFactory.getLogger(QRCodeUtil.class);

    /**
     * CODE_WIDTH:二维码宽度,单位像素
     * CODE_HEIGHT:二维码高度,单位像素
     * FRONT_COLOR:二维码前景色,0x000000 表示黑色
     * BACKGROUND_COLOR:二维码背景色,0xFFFFFF 表示白色
     * 演示用 16 进制表示,和前端页面 CSS 的取色是一样的,注意前后景颜色应该对比明显,如常见的黑白
     */
    private static final int CODE_WIDTH = 400;
    private static final int CODE_HEIGHT = 400;
    private static final int FRONT_COLOR = 0x000000;
    private static final int BACKGROUND_COLOR = 0xFFFFFF;

    /**
     * @param codeContent        二维码参数内容,如果是一个网页地址,如 https://www.baidu.com/ 则 微信扫一扫会直接进入此地址, 如果是一些参数,如
     *                           1541656080837,则微信扫一扫会直接回显这些参数值
     * @param codeImgFileSaveDir 二维码图片保存的目录,如 D:/codes
     * @param fileName           二维码图片文件名称,带格式,如 123.png
     */
    public static void createCodeToFile(String codeContent, File codeImgFileSaveDir, String fileName) {
        try {
            if (codeContent == null || "".equals(codeContent)) {
                log.info("二维码内容为空,不进行操作...");
                return;
            }
            codeContent = codeContent.trim();
            if (codeImgFileSaveDir == null || codeImgFileSaveDir.isFile()) {
                codeImgFileSaveDir = FileSystemView.getFileSystemView().getHomeDirectory();
                log.info("二维码图片存在目录为空,默认放在桌面...");
            }
            if (!codeImgFileSaveDir.exists()) {
                codeImgFileSaveDir.mkdirs();
                log.info("二维码图片存在目录不存在,开始创建...");
            }
            if (fileName == null || "".equals(fileName)) {
                fileName = new Date().getTime() + ".png";
                log.info("二维码图片文件名为空,随机生成 png 格式图片...");
            }

            BufferedImage bufferedImage = getBufferedImage(codeContent);

            /*
             * javax.imageio.ImageIO:java扩展的图像IO
             * write(RenderedImage im, String formatName, File output)
             *       im:待写入的图像, formatName:图像写入的格式,output:写入的图像文件,文件不存在时会自动创建
             */
            File codeImgFile = new File(codeImgFileSaveDir, fileName);
            ImageIO.write(bufferedImage, "png", codeImgFile);

            log.info("二维码图片生成成功:" + codeImgFile.getPath());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 生成二维码并输出到输出流, 通常用于输出到网页上进行显示
     * 输出到网页与输出到磁盘上的文件中,区别在于最后一句 ImageIO.write
     * write(RenderedImage im,String formatName,File output):写到文件中
     * write(RenderedImage im,String formatName,OutputStream output):输出到输出流中
     *
     * @param codeContent  :二维码内容
     * @param outputStream :输出流,比如 HttpServletResponse 的 getOutputStream
     */
    public static void createCodeToOutputStream(String codeContent, OutputStream outputStream) {
        try {
            if (codeContent == null || "".equals(codeContent.trim())) {
                log.info("二维码内容为空,不进行操作...");
                return;
            }
            codeContent = codeContent.trim();

            BufferedImage bufferedImage = getBufferedImage(codeContent);
            /*
             * 区别就是以一句,输出到输出流中,如果第三个参数是 File,则输出到文件中
             */
            ImageIO.write(bufferedImage, "png", outputStream);
            log.info("二维码图片生成到输出流成功...");
        } catch (Exception e) {
            e.printStackTrace();
            log.error("发生错误: {}!", e.getMessage());
        }
    }


    private static BufferedImage getBufferedImage(String codeContent) throws WriterException {
        /*
         * com.google.zxing.EncodeHintType:编码提示类型,枚举类型
         * EncodeHintType.CHARACTER_SET:设置字符编码类型
         * EncodeHintType.ERROR_CORRECTION:设置误差校正
         * ErrorCorrectionLevel:误差校正等级,L = ~7% correction、M = ~15% correction、Q = ~25% correction、H = ~30% correction
         *   不设置时,默认为 L 等级,等级不一样,生成的图案不同,但扫描的结果是一样的
         * EncodeHintType.MARGIN:设置二维码边距,单位像素,值越小,二维码距离四周越近
         */
        Map<EncodeHintType, Object> hints = new HashMap();
        hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
        hints.put(EncodeHintType.MARGIN, 1);

        /*
         * MultiFormatWriter:多格式写入,这是一个工厂类,里面重载了两个 encode 方法,用于写入条形码或二维码
         *      encode(String contents,BarcodeFormat format,int width, int height,Map<EncodeHintType,?> hints)
         *      contents:条形码/二维码内容
         *      format:编码类型,如 条形码,二维码 等
         *      width:码的宽度
         *      height:码的高度
         *      hints:码内容的编码类型
         * BarcodeFormat:枚举该程序包已知的条形码格式,即创建何种码,如 1 维的条形码,2 维的二维码 等
         * BitMatrix:位(比特)矩阵或叫2D矩阵,也就是需要的二维码
         */
        MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
        BitMatrix bitMatrix = multiFormatWriter.encode(codeContent, BarcodeFormat.QR_CODE, CODE_WIDTH, CODE_HEIGHT, hints);

        /*
         * java.awt.image.BufferedImage:具有图像数据的可访问缓冲图像,实现了 RenderedImage 接口
         * BitMatrix 的 get(int x, int y) 获取比特矩阵内容,指定位置有值,则返回true,将其设置为前景色,否则设置为背景色
         * BufferedImage 的 setRGB(int x, int y, int rgb) 方法设置图像像素
         *      x:像素位置的横坐标,即列
         *      y:像素位置的纵坐标,即行
         *      rgb:像素的值,采用 16 进制,如 0xFFFFFF 白色
         */
        BufferedImage bufferedImage = new BufferedImage(CODE_WIDTH, CODE_HEIGHT, BufferedImage.TYPE_INT_BGR);
        for (int x = 0; x < CODE_WIDTH; x++) {
            for (int y = 0; y < CODE_HEIGHT; y++) {
                bufferedImage.setRGB(x, y, bitMatrix.get(x, y) ? FRONT_COLOR : BACKGROUND_COLOR);
            }
        }
        return bufferedImage;
    }


    /**
     * 根据本地二维码图片解析二维码内容 注:图片必须是二维码图片,但也可以是微信用户二维码名片,上面有名称、头像也是可以的)
     *
     * @param file 本地二维码图片文件,如 E:\\logs\\2.jpg
     * @return
     * @throws Exception
     */
    public static String parseQRCodeByFile(File file) {
        String resultStr = null;
        if (file == null || file.isDirectory() || !file.exists()) {
            return resultStr;
        }
        try {
            /*
             * ImageIO的BufferedImage read(URL input)方法用于读取网络图片文件转为内存缓冲图像
             * 同理还有:read(File input)、read(InputStream input)、、read(ImageInputStream stream)
             */
            BufferedImage bufferedImage = ImageIO.read(file);
            /*
             * com.google.zxing.client.j2se.BufferedImageLuminanceSource:缓冲图像亮度源
             * 将 java.awt.image.BufferedImage 转为 zxing 的 缓冲图像亮度源
             * 关键就是下面这几句:HybridBinarizer 用于读取二维码图像数据,BinaryBitmap 二进制位图
             */
            BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(bufferedImage);
            BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
            Hashtable hints = new Hashtable();
            hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
            /*
             * 如果图片不是二维码图片,则 decode 抛异常:com.google.zxing.NotFoundException
             * MultiFormatWriter 的 encode 用于对内容进行编码成 2D 矩阵
             * MultiFormatReader 的 decode 用于读取二进制位图数据
             */
            Result result = new MultiFormatReader().decode(bitmap, hints);
            resultStr = result.getText();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NotFoundException e) {
            e.printStackTrace();
            log.error("图片非二维码图片, 路径是: {}!", file.getPath());
        }
        return resultStr;
    }


    /**
     * 根据网络二维码图片解析二维码内容, 区别仅仅在于 ImageIO.read(url); 这一个重载的方法)
     *
     * @param url 二维码图片网络地址,如 https://res.wx.qq.com/mpres/htmledition/images/mp_qrcode3a7b38.gif
     * @return
     * @throws Exception
     */
    public static String parseQRCodeByUrl(URL url) {
        String resultStr = null;
        if (url == null) {
            return resultStr;
        }
        try {
            /*
             * ImageIO 的 BufferedImage read(URL input) 方法用于读取网络图片文件转为内存缓冲图像
             * 同理还有:read(File input)、read(InputStream input)、、read(ImageInputStream stream)
             * 如果图片网络地址错误,比如不能访问,则 read 抛异常:javax.imageio.IIOException: Can't get input stream from URL!
             */
            BufferedImage bufferedImage = ImageIO.read(url);
            /*
             * com.google.zxing.client.j2se.BufferedImageLuminanceSource:缓冲图像亮度源
             * 将 java.awt.image.BufferedImage 转为 zxing 的 缓冲图像亮度源
             * 关键就是下面这几句:HybridBinarizer 用于读取二维码图像数据,BinaryBitmap 二进制位图
             */
            BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(bufferedImage);
            BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
            Hashtable hints = new Hashtable();
            /*
             * 如果内容包含中文,则解码的字符集格式应该和编码时一致
             */
            hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
            /*
             * 如果图片不是二维码图片,则 decode 抛异常:com.google.zxing.NotFoundException
             * MultiFormatWriter 的 encode 用于对内容进行编码成 2D 矩阵
             * MultiFormatReader 的 decode 用于读取二进制位图数据
             */
            Result result = new MultiFormatReader().decode(bitmap, hints);
            resultStr = result.getText();
        } catch (IOException e) {
            e.printStackTrace();
            log.error("二维码图片地址错误, 地址是: {}!", url);
        } catch (NotFoundException e) {
            e.printStackTrace();
            log.error("图片非二维码图片, 地址是: {}!", url);
        }
        return resultStr;
    }

    private static final int QRCOLOR = 0xFF000000; // 默认是黑色
    private static final int BGWHITE = 0xFFFFFFFF; // 背景颜色
    private static final int WIDTH = 400; // 二维码宽
    private static final int HEIGHT = 400; // 二维码高

    // 用于设置QR二维码参数
    private static Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>() {
        private static final long serialVersionUID = 1L;

        {
            put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);// 设置QR二维码的纠错级别(H为最高级别)具体级别信息
            put(EncodeHintType.CHARACTER_SET, "utf-8");// 设置编码方式
        }
    };

    // 生成带logo的二维码图片
    public static void drawLogoQRCode(File logoFile, OutputStream outputStream, String qrUrl) {
        try {
            MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
            // 参数顺序分别为:编码内容,编码类型,生成图片宽度,生成图片高度,设置参数
            BitMatrix bm = multiFormatWriter.encode(qrUrl, BarcodeFormat.QR_CODE, WIDTH, HEIGHT, hints);
            BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);

            // 开始利用二维码数据创建Bitmap图片,分别设为黑(0xFFFFFFFF)白(0xFF000000)两色
            for (int x = 0; x < WIDTH; x++) {
                for (int y = 0; y < HEIGHT; y++) {
                    image.setRGB(x, y, bm.get(x, y) ? QRCOLOR : BGWHITE);
                }
            }

            int width = image.getWidth();
            int height = image.getHeight();
            if (Objects.nonNull(logoFile) && logoFile.exists()) {
                // 构建绘图对象
                Graphics2D g = image.createGraphics();
                // 读取Logo图片
                BufferedImage logo = ImageIO.read(logoFile);
                // 开始绘制logo图片
                g.drawImage(logo, width * 2 / 5, height * 2 / 5, width * 2 / 10, height * 2 / 10, null);
                g.dispose();
                logo.flush();
            }


            image.flush();

            /*
             * 区别就是以一句,输出到输出流中,如果第三个参数是 File,则输出到文件中
             */
            ImageIO.write(image, "png", outputStream);
        } catch (Exception e) {
            e.printStackTrace();
        }


    }
}

Then create a new Controller, and then create a new interface method

    @GetMapping("/qrcode")
    public void getQRCode(HttpServletResponse response) throws Exception {
        SysAppVersion sysAppVersion = appVersionService.getLast();
        String downloadpath = sysAppVersion.getDownloadLink();/*下载路径*/
        try {
            /*
             * 调用工具类生成二维码并输出到输出流中
             */
            QRCodeUtil.createCodeToOutputStream(downloadpath, response.getOutputStream());
            log.info("成功生成二维码!");
        } catch (IOException e) {
            log.error("发生错误, 错误信息是:{}!", e.getMessage());
        }
    }

In the interface method, call QRCodeUtil.createCodeToOutputStream to set the content to be displayed in the QR code. This method outputs the QR code to the output stream.

Method description:

Generate a QR code and output it to the output stream, usually used for output to the web page for display
. The difference between the output to the web page and the output to the file on the disk is the last sentence ImageIO.write
write(RenderedImage im,String formatName,File output): Write to the file
write(RenderedImage im,String formatName,OutputStream output): output to the output stream

In addition to generating ordinary two-dimensional codes, this tool can also generate two-dimensional codes with log. For details, see the method description of the tool.

Front-end Vue

A new pop-up window is added on the page, and the el-image control is used to display the picture in the pop-up window. The pop-up window is hidden by default

      <el-dialog
        title="请扫描二维码下载"
        width="15%"
        :visible.sync="dialogVisible"
        :before-close="handleClose">
          <el-image
            style="width: 150px; height: 150px"
            :src="fitUrl"
            :fit="fit"></el-image>
      </el-dialog>

These attributes need to be declared in advance

  data() {
    return {
      //返回的文件url
      fileUrl: "",
      dialogVisible:false,
    };

Then add a button to the page

        <el-button
          type="primary"
          icon="el-icon-download"
          size="mini"
          @click="downApp"
          >app下载</el-button>

Button click event in downApp

    downApp(){
        downAppImage().then((val) => {
          const src=window.URL.createObjectURL(val)
          this.fitUrl=src;
          this.dialogVisible=true;
        })
    }

The downAppImage method is called, and the response value val is obtained, and then through window.URL.createObjectURL(val)

Get the picture preview address and assign it to the el-image picture data source variable fitUrl, and then let the window display.

Where downAppImage is passed

import { downAppImage } from "@/api/tool/edition.js";

The introduced js method

    export function downAppImage(){
        return request({
            url: '/download/qrcode',
            method:'get',
            responseType:"blob"
        })
    }

This method will call the previous SpringBoot backend interface. Note that the response type here is blob

Then window.URL.createObjectURL instructions:

The URL.createObjectURL() static method creates a DOMString that contains a URL representing the object given in the parameter. The life cycle of this URL is bound to the document in the window where it was created. This new URL object represents the specified File object or Blob object.

effect

Call the background interface, the background will generate a response stream of the QR code image

 

Then the front end calls the interface

 

After calling window.URL.createObjectURL(val)

 

Guess you like

Origin blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/114086145