Java如何生成花里胡哨的二维码

简介: 目录一、序言二、找资料1、寻觅文档2、寻觅代码三、代码示例1、简单的二维码2、带颜色的二维码3、带logo的二维码四、工具类封装

目录


一、序言


之前在做头马演讲俱乐部哼哈官可视化汇报报告时,为了方便大家移动端查看可视化报告,而不是通过点击链接这种生硬的方式,专门研究了下如何生成二维码。


在Github上有个第三方库,来自google的 zxing,这个库能生成各种条形码,如:二维码、支付条形码等。下面是zxing支持的一些格式:

c0e8df48ec60431a9eb3c7a57e45c678.png

今天我们重点研究一下关于通过zxing生成二维码的一些参数,以及从哪里找到这些文档和介绍。

二、找资料


1、寻觅文档

有点小尴尬的是,zxing生成二维码Java相关的API并没有明确的相关解释以及使用示例,笔者也是在zxing的github上发现了一个在线生成二维码的网站,在线二维码生成器bb608160c0cc442c80aedcfd084fe031.png备注:这个网站需要翻墙,不然访问不了。8fd20718ca394146a7f5bae71011bb79.png这里重点介绍一下图中error_correction_level 这个参数,二维码支持4种级别的错误校正,错误校正主要用来做缺失、误读或者遮盖数据修复。二维码越冗余也就意味着只能存储更少的数据,错误校正级别主要有如下4级:


L - 默认级别,可以做到即使丢失7%的数据也能正常识别。

M - 中级,可以做到即使丢失15%的数据也能正常识别。

Q - 中上级,可以做到即使丢失25%的数据也能正常识别。

H - 高级,可以做到即使丢失30%的数据也能正常识别。

备注:所谓错误校正,说句大白话就是如果二维码有缺失,或者部分二维码有遮挡,还能不能被正常识别。级别越高,容错率也就越高。

当我们用不同级别生成二维码时,二维码的密集度也会不一样,错误校正级别越高,生成的二维码越分散。


2、寻觅代码

前面那个需要翻墙的在线二维码生成器实际上就是一个古老的Servlet应用,在zxing的源码里找到zxingorg这个目录,这就是那个在线二维码生成器的源码了。生成二维码的核心逻辑在ChartServlet这个类里,如下:

a09234f932da4d1f99d03f37b388f636.png接下来我们直接在doEncode方法里就能找到生成二维码的逻辑,还是非常简单的,代码如下:

private static void doEncode(HttpServletRequest request, HttpServletResponse response, boolean isPost)
      throws IOException {
    ChartServletRequestParameters parameters;
    try {
      parameters = doParseParameters(request, isPost);
    } catch (IllegalArgumentException | NullPointerException e) {
      response.sendError(HttpServletResponse.SC_BAD_REQUEST, e.toString());
      return;
    }
    Map<EncodeHintType,Object> hints = new EnumMap<>(EncodeHintType.class);
    hints.put(EncodeHintType.MARGIN, parameters.getMargin());
    if (!StandardCharsets.ISO_8859_1.equals(parameters.getOutputEncoding())) {
      // Only set if not QR code default
      hints.put(EncodeHintType.CHARACTER_SET, parameters.getOutputEncoding().name());
    }
    hints.put(EncodeHintType.ERROR_CORRECTION, parameters.getEcLevel());
    BitMatrix matrix;
    try {
      matrix = new QRCodeWriter().encode(parameters.getText(),
                                         BarcodeFormat.QR_CODE,
                                         parameters.getWidth(),
                                         parameters.getHeight(),
                                         hints);
    } catch (WriterException we) {
      response.sendError(HttpServletResponse.SC_BAD_REQUEST, we.toString());
      return;
    }
    String requestURI = request.getRequestURI();
    if (requestURI == null) {
      response.sendError(HttpServletResponse.SC_BAD_REQUEST);
      return;
    }
    int lastDot = requestURI.lastIndexOf('.');
    String imageFormat;
    if (lastDot > 0) {
      imageFormat = requestURI.substring(lastDot + 1).toUpperCase(Locale.ROOT);
      // Special-case jpg -> JPEG
      if ("JPG".equals(imageFormat)) {
        imageFormat = "JPEG";
      }
    } else {
      imageFormat = "PNG";
    }
    String contentType;
    switch (imageFormat) {
      case "PNG":
        contentType = "image/png";
        break;
      case "JPEG":
        contentType = "image/jpeg";
        break;
      case "GIF":
        contentType = "image/gif";
        break;
      default:
        throw new IllegalArgumentException("Unknown format " + imageFormat);
    }
    ByteArrayOutputStream imageOut = new ByteArrayOutputStream(1024);
    MatrixToImageWriter.writeToStream(matrix, imageFormat, imageOut);
    byte[] imageData = imageOut.toByteArray();
    response.setContentType(contentType);
    response.setContentLength(imageData.length);
    response.setHeader("Cache-Control", "public");
    response.getOutputStream().write(imageData);
  }

三、代码示例


首先引入zxing的依赖,如下:

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

1、简单的二维码

由于平台上二维码图片会被当做违规,无法查看,因此这里就不贴二维码示例了。

Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.CHARACTER_SET, StandardCharsets.UTF_8.name());
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
String content = "https://www.baidu.com";
String qrCodePath = "C:\\Users\\mc\\Desktop\\qrcode.png";
QRCodeWriter qrCodeWriter = new QRCodeWriter();
BitMatrix bitMatrix = qrCodeWriter.encode(content, BarcodeFormat.QR_CODE, 300, 300, hints);
MatrixToImageWriter.writeToPath(bitMatrix, "png", Paths.get(qrCodePath));

2、带颜色的二维码

Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.CHARACTER_SET, StandardCharsets.UTF_8.name());
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
String content = "https://www.baidu.com";
String qrCodePath = "C:\\Users\\mc\\Desktop\\qrcode.png";
// 生成带颜色的二维码, 指定前景色和后景色即可
MatrixToImageConfig matrixToImageConfig = new MatrixToImageConfig(Color.GREEN.getRGB(), Color.WHITE.getRGB());
QRCodeWriter qrCodeWriter = new QRCodeWriter();
BitMatrix bitMatrix = qrCodeWriter.encode(content, BarcodeFormat.QR_CODE, 300, 300, hints);
MatrixToImageWriter.writeToPath(bitMatrix, "png", Paths.get(qrCodePath), matrixToImageConfig);

3、带logo的二维码


Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.CHARACTER_SET, StandardCharsets.UTF_8.name());
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
String content = "https://www.baidu.com";
String qrCodePath = "C:\\Users\\mc\\Desktop\\qrcode.png";
String logoPath = "C:\\Users\\mc\\Desktop\\logo_mini.png";
QRCodeWriter qrCodeWriter = new QRCodeWriter();
BitMatrix bitMatrix = qrCodeWriter.encode(content, BarcodeFormat.QR_CODE, 300, 300, hints);
// 写入输出流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
MatrixToImageWriter.writeToStream(bitMatrix, DEFAULT_FORMAT, baos);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
BufferedImage qrCodeImage = ImageIO.read(bais);
Graphics2D graphics2D = qrCodeImage.createGraphics();
BufferedImage logoImage = ImageIO.read(Paths.get(logoPath).toFile());
// 这里将logo的位置居中
int logoX = (qrCodeImage.getWidth() - logoImage.getWidth()) / 2;
int logoY = (qrCodeImage.getHeight() - logoImage.getHeight()) / 2;
// 在二维码画布上绘图
graphics2D.drawImage(logoImage, null, logoX, logoY);
graphics2D.dispose();
// 输出绘制后logo的二维码图片
ImageIO.write(qrCodeImage, "png", Paths.get(qrCodePath).toFile());

备注:这里需要注意一下logo的图片尺寸,占用太大会导致二维码无法识别,同时最好将error_correction_level设置为高级,增加容错率。

四、工具类封装


核心代码比较简单,随手简单封装了一下,zxing还是很强大的,点个赞。

/**
 * @author 刘亚楼
 * @date 2022/9/22
 */
public abstract class QrCodeUtils {
  /**
   * 默认生成的文件
   */
  private static final String DEFAULT_FORMAT = "png";
  /**
   * 默认额外参数:可设置字符集、容错级别
   */
  private static final Map<EncodeHintType, Object> DEFAULT_HINTS = new HashMap<>();
  static {
    DEFAULT_HINTS.put(EncodeHintType.CHARACTER_SET, StandardCharsets.UTF_8.name());
    DEFAULT_HINTS.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
  }
  /**
   * 生成绿码
   * @param content 二维码展示内容
   * @param width 二维码图片宽度
   * @param height 二维码图片高度
   * @return 图片二进制流
   * @throws Exception
   */
  public static byte[] generateGreenQrCodeAsByteArray(String content, int width, int height) throws Exception {
    return generateQrCodeAsByteArray(content, width, height, Color.GREEN.getRGB(), Color.WHITE.getRGB());
  }
  /**
   * 生成二维码并转为二进制流
   * @param content 二维码展示内容
   * @param width 二维码图片宽度
   * @param height 二维码图片高度
   * @param onColorAsRGB 二维码数据图案颜色
   * @param offColorAsRGB 二维码背景色
   * @return 图片二进制流
   * @throws Exception
   */
  public static byte[] generateQrCodeAsByteArray(String content, int width, int height, int onColorAsRGB, int offColorAsRGB) throws Exception {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    generateQrCodeAsByteArray(baos, content, width, height, onColorAsRGB, offColorAsRGB);
    return baos.toByteArray();
  }
  /**
   * 生成二维码
   * @param os 输出流
   * @param content 二维码展示内容
   * @param width 二维码图片宽度
   * @param height 二维码图片高度
   * @param onColorAsRGB 二维码数据图案颜色
   * @param offColorAsRGB 二维码背景色
   * @throws Exception
   */
  public static void generateQrCodeAsByteArray(OutputStream os, String content, int width, int height, int onColorAsRGB, int offColorAsRGB) throws Exception {
    QRCodeWriter qrCodeWriter = new QRCodeWriter();
    BitMatrix bitMatrix = qrCodeWriter.encode(content, BarcodeFormat.QR_CODE, width, height, DEFAULT_HINTS);
    MatrixToImageConfig matrixToImageConfig = new MatrixToImageConfig(onColorAsRGB, offColorAsRGB);
    MatrixToImageWriter.writeToStream(bitMatrix, DEFAULT_FORMAT, os, matrixToImageConfig);
  }
  /**
   * 生成二维码
   * @param path 文件路径
   * @param content 二维码展示内容
   * @param width 二维码图片宽度
   * @param height 二维码图片高度
   * @param onColorAsRGB 二维码数据图案颜色
   * @param offColorAsRGB 二维码背景色
   * @throws Exception
   */
  public static void generateQrCodeAsByteArray(String path, String content, int width, int height, int onColorAsRGB, int offColorAsRGB) throws Exception {
    QRCodeWriter qrCodeWriter = new QRCodeWriter();
    BitMatrix bitMatrix = qrCodeWriter.encode(content, BarcodeFormat.QR_CODE, width, height, DEFAULT_HINTS);
    MatrixToImageConfig matrixToImageConfig = new MatrixToImageConfig(onColorAsRGB, offColorAsRGB);
    MatrixToImageWriter.writeToPath(bitMatrix, DEFAULT_FORMAT, Paths.get(path), matrixToImageConfig);
  }
  public static byte[] attachLogoInTheMiddle(InputStream qrCodeIs, InputStream logoIs) throws Exception {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    attachLogoInTheMiddle(qrCodeIs, logoIs, baos);
    return baos.toByteArray();
  }
  /**
   * 二维码中间插入logo
   * @param qrCodeIs 二维码输入流
   * @param logoIs logo输入流
   * @param dest 输出路径
   * @throws Exception
   */
  public static void attachLogoInTheMiddle(InputStream qrCodeIs, InputStream logoIs, OutputStream dest) throws Exception {
    BufferedImage qrCodeImage = ImageIO.read(qrCodeIs);
    BufferedImage logoImage = ImageIO.read(logoIs);
    Graphics2D graphics2D = qrCodeImage.createGraphics();
    // 这里将logo的位置居中
    int logoX = (qrCodeImage.getWidth() - logoImage.getWidth()) / 2;
    int logoY = (qrCodeImage.getHeight() - logoImage.getHeight()) / 2;
    graphics2D.drawImage(logoImage, null, logoX, logoY);
    graphics2D.dispose();
    ImageIO.write(qrCodeImage, DEFAULT_FORMAT, dest);
  }
  /**
   * 重置图片大小
   * @param rawImage 原图片
   * @param targetWidth 目标图片宽度
   * @param targetHeight 目标图片高度
   * @return
   */
  public static BufferedImage resizeImage(BufferedImage rawImage, int targetWidth, int targetHeight) {
    Image scaledImage = rawImage.getScaledInstance(targetWidth, targetHeight, Image.SCALE_AREA_AVERAGING);
    BufferedImage resizedImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
    resizedImage.getGraphics().drawImage(scaledImage, 0, 0, null);
    return resizedImage;
  }
}
相关文章
|
3月前
|
前端开发 小程序 Java
(JAVA)获取支付宝二维码 带参数
(JAVA)获取支付宝二维码 带参数
|
4月前
|
Java Maven C++
Java在后台获取USB二维码扫描枪扫描的内容
在Java Web项目中集成扫描枪,首先发现扫描枪可模拟键盘输入,聚焦窗口即接收数据。通过官网电话得知有串口编程支持,但项目无串口,故搜索Java串口解决方案。找到开源项目,理解其工作原理。使用Java Native Access (JNA)来监听操作系统输入,解决Java JVM无法直接获取键盘输入的问题。遇到jar包缺失问题,最终在国内仓库mvnjar.com找到合适版本运行成功。实现时需注意处理字母大小写和特殊字符输入。
210 4
|
4月前
|
存储 前端开发 Java
Java生成二维码
Java生成二维码
|
4月前
|
存储 Java Maven
Java能这么轻松识别二维码
Java能这么轻松识别二维码
782 1
|
4月前
|
Java Maven
Java生成二维码
Java生成二维码
48 0
|
10月前
|
前端开发 Java Maven
Java代码生成二维码
Java代码生成二维码
108 0
|
JSON 小程序 Java
Java 获取小程序二维码的几种方式
Java 获取小程序二维码的几种方式
|
机器学习/深度学习 Java 数据安全/隐私保护
你有没有使用java生成过二维码?(二)
你有没有使用java生成过二维码?(二)
|
机器学习/深度学习 Java 数据安全/隐私保护
你有没有使用java生成过二维码?(一)
你有没有使用java生成过二维码?(一)
java语言使用多线程导出二维码
java语言使用多线程导出二维码