VUE+Springboot实现生成二维码及二维码下载功能

2024-01-04

一、Springboot相关

1、pom依赖引入

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

2、代码实现

import com.google.zxing.BarcodeFormat;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;

/**
     * 生成图片二维码
     *
     * @param content 二维码内容
     * @return
     */
    private String buildQRCode(String content, String[] arr) {
        ByteArrayOutputStream outputStream = null;
        ByteArrayInputStream in = null;
        ByteArrayOutputStream outputStream1 = null;
        try {
            QRCodeWriter qrCodeWriter = new QRCodeWriter();
            BitMatrix bitMatrix = qrCodeWriter.encode(content, BarcodeFormat.QR_CODE, 1000, 1000);

            //去掉二维码白边
            int[] rec = bitMatrix.getEnclosingRectangle();
            int resWidth = rec[2] + 1;
            int resHeight = rec[3] + 1;

            BitMatrix resMatrix = new BitMatrix(resWidth, resHeight);
            resMatrix.clear();
            for (int i = 0; i < resWidth; i++) {
                for (int j = 0; j < resHeight; j++) {
                    if (bitMatrix.get(i + rec[0], j + rec[1]))
                        resMatrix.set(i, j);
                }
            }
            //去掉二维码白边  结束

            outputStream = new ByteArrayOutputStream();
            MatrixToImageWriter.writeToStream(resMatrix, "PNG", outputStream);

            int width = 1000;
            int height = 1500;
            outputStream1 = new ByteArrayOutputStream();
            in = new ByteArrayInputStream(outputStream.toByteArray());
            BufferedImage image = ImageIO.read(in);
            BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            Graphics2D g2 = bi.createGraphics();


//            //开始绘图 这版设置透明似乎有点点问题  暂时注掉
//            g2.setBackground(Color.WHITE);
//            g2.clearRect(0, 0, width, height);
//            g2.setPaint(new Color(255, 255, 255,1));      //设置背景颜色
//            g2.fillRect(0, 0, width, height);  //填充颜色
//
//            //设置透明  start
//            bi = g2.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
//            g2 = bi.createGraphics();
//            //设置透明  end

            g2.setBackground(Color.WHITE);
            g2.clearRect(0, 0, width, height);

            /** 设置生成图片的文字样式 * */
            Font font = new Font("黑体", Font.BOLD, 80);
            g2.setFont(font);
            g2.setPaint(Color.BLACK);

            //生成头部文字
            /** 防止生成的文字带有锯齿 * */
            g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

            FontRenderContext context = g2.getFontRenderContext();
            Rectangle2D bounds = font.getStringBounds(QR_CODE_HEAD_STR, context);
            double headX = (width - bounds.getWidth()) / 2;
            /** 在图片上生成文字 * */
            g2.drawString(QR_CODE_HEAD_STR, (int) headX, 80);

            g2.drawImage(image, 0, 165, width - 1, height - 520, null); //这里减去25是为了防止字和图重合

            font = new Font("黑体", Font.BOLD, 60);
            g2.setFont(font);

            double i = 2.5;
            for (String str : arr) {
                /** 设置字体在图片中的位置 在这里是居中* */
                context = g2.getFontRenderContext();
                bounds = font.getStringBounds(str, context);
                double x = (width - bounds.getWidth()) / 2;
                double y = (height - 75 - bounds.getHeight());
                double ascent = -bounds.getY() - bounds.getHeight() * i - 9 * i;
                double baseY = y + ascent;

                /** 防止生成的文字带有锯齿 * */
                g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

                /** 在图片上生成文字 * */
                g2.drawString(str, (int) x, (int) baseY);

                i--;
            }

            //todo 放置尾部logo
            Response response = fileUploadFeignService.getFileInputStream("img/logopng.png");
            BufferedImage tailImage = ImageIO.read(response.body().asInputStream());
            //缩小图片尺寸
            g2.drawImage(tailImage, (width - tailImage.getWidth() / 3) / 2 + 5, 1400, tailImage.getWidth() / 3, tailImage.getHeight() / 3, null);

            int c = bi.getRGB(999, 1100);

            BufferedImage tmpImg = new BufferedImage(width, height,BufferedImage.TYPE_4BYTE_ABGR);//新建一个类型支持透明的BufferedImage
            for(int k = 0; k < width; ++k)//把原图片的内容复制到新的图片,同时把背景设为透明
            {
                for(int j = 0; j < height; ++j){
                    //把背景设为透明
                    if(bi.getRGB(k, j) == c){
                        tmpImg .setRGB(k, j, c & 0x00ffffff);
                    }
                    //设置透明度
                    else{
                        int rgb = tmpImg .getRGB(k, j);
                        rgb = ((10 * 255 / 10) << 24) | (rgb & 0x00ffffff);
                        tmpImg .setRGB(k, j, rgb);
                    }
                }
            }

            g2.dispose();
            ImageIO.write(tmpImg, "PNG", outputStream1);

            Base64.Encoder encoder = Base64.getEncoder();

            String text = encoder.encodeToString(outputStream1.toByteArray());
            return text;
        } catch (WriterException e) {
            e.printStackTrace();
            return "";
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        } finally {
            try {
                if (outputStream != null) {
                    outputStream.close();
                }

                if (in != null) {
                    in.close();
                }

                if (outputStream1 != null) {
                    outputStream1.close();
                }

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

content:二维码内容

arr:二维码图片上要显示的内容


//todo 放置尾部logo
Response response = fileUploadFeignService.getFileInputStream("img/logopng.png");  

3、controller调用‘

public AjaxResult getInfo()
    {
        AjaxResult ajax = AjaxResult.success();
            ajax.put("img", "data:image/png;base64," + buildQRCode(qrCodeContent, qrCodeMessage.toArray(new String[qrCodeMessage.size()])));
        }
        return ajax;
    }

二、vue页面

1、vue弹出二维码页面

<el-dialog :title="title" :visible.sync="openLabel" :close-on-click-modal="false">
      <el-form ref="form" :model="form" :rules="rules" label-width="120px">
        <el-row v-show="false">
          <el-col>
            <el-form-item label="电子标签码" prop="electronicLabelCode">
              <el-input v-model="form.electronicLabelCode" placeholder="请输入电子标签码" :disabled="isDisabled"/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col>
            <el-form-item prop="file" ref="uploadElement" label="二维码">
              <el-upload
                class="avatar-uploader"
                action="#"
                :show-file-list="false"
                :disabled="true">
                <el-image v-if="dangerousImageUrl" :src="dangerousImageUrl" class="avatar">
                  <div slot="error" class="image-slot">
                    <i class="el-icon-picture-outline"></i>
                  </div>
                </el-image>
                <i v-else class="el-icon-plus avatar-uploader-icon"></i>
              </el-upload>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="">
              <el-link @click="downloadQRCode" :disabled="isDisabled">下载二维码图片</el-link>
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="submitForm" v-if="!isDisabled" v-show="false">保 存</el-button>
        <el-button @click="cancel">取 消</el-button>
      </div>
    </el-dialog>

dangerousImageUrl :

请求后端接口响应

this.dangerousImageUrl = response.img;

2、二维码图片下载

//二维码图片下载
    downloadQRCode() {
      let aLink = document.createElement('a');
      let blob = this.base64ToBlob(this.dangerousImageUrl); //new Blob([content]);

      let evt = document.createEvent("HTMLEvents");
      evt.initEvent("click", true, true);//initEvent 不加后两个参数在FF下会报错  事件类型,是否冒泡,是否阻止浏览器的默认行为
      aLink.download = "group_" + new Date().getTime() + ".png";
      aLink.href = URL.createObjectURL(blob);

      // aLink.dispatchEvent(evt);
      aLink.click();
    },

    base64ToBlob(code) {
      let parts = code.split(';base64,');
      let contentType = parts[0].split(':')[1];
      let raw = window.atob(parts[1]);
      let rawLength = raw.length;

      let uInt8Array = new Uint8Array(rawLength);

      for (let i = 0; i < rawLength; ++i) {
        uInt8Array[i] = raw.charCodeAt(i);
      }
      return new Blob([uInt8Array], { type: contentType });
    },

三、data: image/png; base64 用法详解

data:image/png;base64, 字符串... 这个表示什么意思,又有什么作用呢?
其实这就是所谓的 Data URI scheme。 直译过来的意思是:URI 数据处理方案...

1. Data URI scheme 是什么 ?

1)Data URI scheme 是在 RFC2397 中定义的,目的是将一些小的数据,直接嵌入到网页中,从而不用再从外部文件载入。减少对 HTTP 的请求次数。达到优化网页的效果。

2)base64 后面那一串字符,其实是一张图片,将这些字符串复制粘贴到浏览器的中打开,就能看到图片了

2. Data URI scheme 的语法

在上面的 Data URI scheme 中:

data 表示取得数据的协定名称;

image/png 是数据类型名称;

base64 是数据的编码方法,逗号后面就是这个image/png文件base64编码后的数据。

目前,Data URI scheme支持的类型有:

data: 文本数据
data: text/plain, ------- 文本数据
data: text/html, -------- HTML代码
data: text/html;base64, -------- base64编码的HTML代码
data: text/css, ---------- CSS代码
data: text/css;base64, ---------- base64编码的CSS代码
data: text/javascript, ------------ Javascript代码
data: text/javascript;base64, --------- base64编码的Javascript代码
data: image/gif;base64, ---------------- base64编码的gif图片数据
data: image/png;base64, -------------- base64编码的png图片数据
data: image/jpeg;base64, ------------- base64编码的jpeg图片数据
data: image/x-icon;base64, ---------- base64编码的icon图片数据

3. Data URI scheme 的优缺点

优点:

减少HTTP请求数,没有了TCP连接消耗和同一 域名 下浏览器的并发数限制。对于小文件会降低带宽。虽然编码后数据量会增加,但是却减少了http头,当http头的数据量大于文件编码的增量,那么就会降低带宽。 对于HTTPS站点,HTTPS和HTTP混用会有 安全 提示,而HTTPS相对于HTTP来讲开销要大更多,所以Data URI在这方面的优势更明显。可以把整个多媒体页面保存为一个文件。

缺点:

  1. 无法被重复利用,同一个文档多次被应用到同一内容中,数据被大量增加,消耗了下载时间。
  2. 无法被独自缓存,其包含文档重新加载时,它也要重新加载。
  3. 耗时,客户端需要重新解码和显示,增加消耗。
  4. 不支持数据压缩,base64编码会增加1/3大小,而urlencode后数据量会增加更多
  5. 安全 ,不利于安全软件的过滤,同时也存在一定的安全隐患。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

VUE+Springboot实现生成二维码及二维码下载功能 的相关文章

随机推荐

  • 智康护智慧养老院建设之养老院护工的排班系统

    智康护是一种针对养老院护工的排班系统 主要目的是优化养老院护工的排班管理 提高护工工作效率并降低工作负担 下面从排班调休 人员定位 排班班次和工单申请等几个方面来阐述智康护的功能 排班调休 智康护系统可以根据养老院的护工资源和护理需求 智能
  • iOS手机查看蓝牙底层日志

    文章目录 一 需要的环境 二 在iOS设备上安装Profiles文件 三 安装Xcode 四 安装packetLogger 五 启动蓝牙数据包分析 参考文档 A New Way to Debug iOS Bluetooth Applicat
  • 用友签约新奥集团,共建智慧资产管理平台

    近日 用友成功签约新奥集团数智资产项目 项目将以数据驱动 资产创值为牵引 将全集团所有板块的资产数据按照标既定模型全面可视化展示 通过数据分析 模型沉淀 智能识别低效闲置资产 预警 赋能资产处置 盘点等事项形成处理闭环 通过数智驱动处置等活
  • 振弦采集仪在地基沉降监测中的应用研究

    振弦采集仪在地基沉降监测中的应用研究 振弦采集仪是一种专门用于测量地基沉降的仪器 它采用振弦原理来测量地基的沉降情况 振弦采集仪通过在地基上安装一根细长的弹性振弦 并测量振弦的变形来获得地基沉降的数据 在地基沉降监测中 振弦采集仪可以提供精
  • promethues grafana 安装和使用

    文章目录 1 promethues安装 2 node exporter安装 3 grafana安装 4 配置promethues监控node节点 5 grafana操作 外传 Docker 镜像下载地址 https hub docker c
  • uniapp微信小程序投票系统实战 (SpringBoot2+vue3.2+element plus ) -小程序端TabBar搭建

    锋哥原创的uniapp微信小程序投票系统实战 uniapp微信小程序投票系统实战课程 SpringBoot2 vue3 2 element plus 火爆连载更新中 哔哩哔哩 bilibili uniapp微信小程序投票系统实战课程 Spr
  • LONGQLORA: EFFICIENT AND EFFECTIVE METHOD TO EXTEND CONTEXT LENGTH OF LARGE LANGUAGE MODELS

    本文是LLM系列文章 针对 LONGQLORA EFFICIENT AND EFFECTIVE METHOD TO EXTEND CONTEXT LENGTH OF LARGE LANGUAGE MODELS 的翻译 LONGQLORA 扩
  • 新规施行推动数据资产化迈出关键一步

    经济参考报 1月2日刊发文章 新规施行推动数据资产化迈出关键一步 文章称 2024年1月1日起 企业数据资源相关会计处理暂行规定 下称 暂行规定 正式施行 暂行规定 明确数据资源的确认范围和会计处理适用准则等 业内人士表示 这是推动数据资产
  • PD SINK协议芯片系列产品介绍对比-ECP5701、FS312A、CH221K、HUSB238、AS225KL

    目录 一 ECP5701 二 FS312A 三 CH221K 四 HUSB238 五 AS225KL 在如今快节奏生活不断蔓延的背景下 人们对各种事情的处理也渐渐地开始要求在保证质量的情况下 不断加快 手机快充就是一个典型的例子 从开始的1
  • 智康护智慧养老院建设之智慧视频联动解决方案

    智康护智慧养老院建设之智慧视频联动解决方案是一种基于智能技术和视频监控的创新解决方案 旨在提升养老院的安全管理和服务质量 通过视频联动实现智能化的监控和应急响应 建设背景 随着人口老龄化问题的日渐严重 养老院作为提供长期照护和安居服务的场所
  • 程序员思维——四个思考原则

    一 什么是四个思考原则 以终为始 确定好真实目标 任务分解 找到实施路径 沟通反馈 解决与人打交道出现的问题 自动化 解决与机器打交道出现的问题 二 如何运用思考框架 运用这个思考框架 我们需要问自己一些问题 Where are we 我们
  • Qt学习_17_一些关于QTableWidget的记录

    1 QTableWidget clear 程序异常退出 近日 项目中使用到QTableWidget 遇到一个问题 项目需要清空这个表格 但是无论调用clear clearContents 程序都报 程序异常退出 而且项目程序还比较多 最开始
  • prometheus grafana mysql监控配置使用

    文章目录 前传 bitnami mysqld exporter 0 15 1镜像 出现了问题 my cnf 可以用这个 prom mysqld exporter v0 15 0 镜像 重要的事情 mysql监控效果 外传 前传 promet
  • 第九章 1 面向对象程序设计

    两大编程思想 面向过程和对象 p108 面向过程 功能上的封装 面向对象 属性和行为上的封装 面向过程 面向对象 区别 事物比较简单 可以用线性的思维去解决 事物比较复杂 使用简单的线性思维无法解决 共同点 1 面向过程和面向对象都是解决实
  • Android跨进程渲染

    文章目录 背景 实现步骤 服务端 客户端 参考代码
  • Python+Selenium键盘鼠标模拟事件操作详解

    当我们定位到具体的一个元素的时候就可以对这个元素进行具体的操作 比如之前章节所执行的 click 操作 这是最简单的操作 webdriver 还有其他的操作 比如元素的基本操作 点击 输入 清除 还有一些高级操作如鼠标键盘模拟事件 弹出框处
  • 服务器3M固定带宽什么意思?够用吗?

    云服务器3M固定带宽是什么意思 速度快吗 3M固定带宽是指云服务器的公网带宽 用于在外网提供服务的 3M带宽的下载速度是384KB 秒 上传速度是1280KB 秒 对于个人博客或流量不多的企业官网速度还是挺快的 阿里云服务器网aliyunf
  • thinkadmin安装步骤

    一 先cmd运行安装命令 创建项目 需要在英文目录下面执行 composer create project zoujingli thinkadmin 二 在confing中的database php配置数据库 三 将仓库的data复制到ap
  • 亚马逊自养号测评防关联技巧分享,亚马逊自养号怎么养?

    我们做亚马逊的都知道 想要做好亚马逊 测评是免不了的 很多卖家选择自养号这种方式 但是亚马逊养号并不是一件容易的事 需要我们提高养号的技术和掌握相应的技巧 而且随着平台审查力度的加强 自养号的账号关联问题也给卖家们带来许多困扰 那么什么是自
  • VUE+Springboot实现生成二维码及二维码下载功能

    一 Springboot相关 1 pom依赖引入