当前位置: 首页 > news >正文

Java通过Zxing生成和解析二维码

java在对二维码进行操作的工具jar来源主要有两种,一种google开发提供的Zxing相关jar,一种是小日本开发提供的Qrcode.jar。以下将对二维码的生成和解析进行详解。
本例中采用google提供的二维码生成工具jar进行操作
 

生成二维码:

在贴出demo代码之前,我们先把其中会用到的部分工具类源码贴出来。

矩阵输出为图片流工具类MatrixToImageWriter:

package com.google.zxing.client.j2se;

import com.google.zxing.common.BitMatrix;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Path;
import javax.imageio.ImageIO;

public final class MatrixToImageWriter
{
  private static final MatrixToImageConfig DEFAULT_CONFIG = new MatrixToImageConfig();
  
  public static BufferedImage toBufferedImage(BitMatrix matrix)
  {
    return toBufferedImage(matrix, DEFAULT_CONFIG);
  }
  
  public static BufferedImage toBufferedImage(BitMatrix matrix, MatrixToImageConfig config)
  {
    int width = matrix.getWidth();
    int height = matrix.getHeight();
    BufferedImage image = new BufferedImage(width, height, config.getBufferedImageColorModel());
    int onColor = config.getPixelOnColor();
    int offColor = config.getPixelOffColor();
    for (int x = 0; x < width; x++) {
      for (int y = 0; y < height; y++) {
        image.setRGB(x, y, matrix.get(x, y) ? onColor : offColor);
      }
    }
    return image;
  }
  
  @Deprecated
  public static void writeToFile(BitMatrix matrix, String format, File file)
    throws IOException
  {
    writeToPath(matrix, format, file.toPath());
  }
  
  public static void writeToPath(BitMatrix matrix, String format, Path file)
    throws IOException
  {
    writeToPath(matrix, format, file, DEFAULT_CONFIG);
  }
  
  @Deprecated
  public static void writeToFile(BitMatrix matrix, String format, File file, MatrixToImageConfig config)
    throws IOException
  {
    writeToPath(matrix, format, file.toPath(), config);
  }
  
  public static void writeToPath(BitMatrix matrix, String format, Path file, MatrixToImageConfig config)
    throws IOException
  {
    BufferedImage image = toBufferedImage(matrix, config);
    if (!ImageIO.write(image, format, file.toFile())) {
      throw new IOException("Could not write an image of format " + format + " to " + file);
    }
  }
  
  public static void writeToStream(BitMatrix matrix, String format, OutputStream stream)
    throws IOException
  {
    writeToStream(matrix, format, stream, DEFAULT_CONFIG);
  }
  
  public static void writeToStream(BitMatrix matrix, String format, OutputStream stream, MatrixToImageConfig config)
    throws IOException
  {
    BufferedImage image = toBufferedImage(matrix, config);
    if (!ImageIO.write(image, format, stream)) {
      throw new IOException("Could not write an image of format " + format);
    }
  }
}

二维码矩阵图片配置类MatrixToImageConfig(这里主要配置背景及条码颜色):

package com.google.zxing.client.j2se;

public final class MatrixToImageConfig
{
  public static final int BLACK = -16777216;
  public static final int WHITE = -1;
  private final int onColor;
  private final int offColor;
  
  public MatrixToImageConfig()
  {
    this(-16777216, -1);
  }
  
  public MatrixToImageConfig(int onColor, int offColor)
  {
    this.onColor = onColor;
    this.offColor = offColor;
  }
  
  public int getPixelOnColor()
  {
    return this.onColor;
  }
  
  public int getPixelOffColor()
  {
    return this.offColor;
  }
  
  int getBufferedImageColorModel()
  {
    if ((this.onColor == -16777216) && (this.offColor == -1)) {
      return 12;
    }
    if ((hasTransparency(this.onColor)) || (hasTransparency(this.offColor))) {
      return 2;
    }
    return 1;
  }
  
  private static boolean hasTransparency(int argb)
  {
    return (argb & 0xFF000000) != -16777216;
  }
}


生成二维码代码如下:

@Test  
public void testEncode() throws WriterException, IOException {  
	String content = "http://www.baidu.com";//这里content为二维码数据。为地址将直接访问,为text将直接解析
	int width = 200; // 图像宽度
	int height = 200; // 图像高度
	String format = "png";// 图像类型
	Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>();
	hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
	hints.put(EncodeHintType.MARGIN, 4);//设置二维码边的空度,非负数,默认值为4。
	BitMatrix bitMatrix = new MultiFormatWriter().encode(content,BarcodeFormat.QR_CODE, width, height, hints);// 生成矩阵
	//MatrixToImageWriter提供多种方式输出二维码,并同时提供二维码其他信息配置的重载方法
	MatrixToImageWriter.writeToFile(bitMatrix, format, new File("d:/qrcode_test.png"));// 输出图像
}

到此,二维码的生成就完成了。

但是,如果要在二维码上添加logo图片,则上述的方法是做不到的,需要自定义一些方法,例如:logo图片的缩放(是否等比例和不等比例情况是否补白)、边框处理、整合到二维码矩阵和颜色设置。如下所示:

logo图片处理代码如下。

/**
 * 把传入的原始图像按高度和宽度进行等比例缩放,生成符合要求的图标
 * 
 * @param srcImageFile 源文件地址
 * @param width 目标宽度
 * @param height 目标高度
 * @param hasFiller 比例不对时是否需要补白:true为补白; false为不补白;
 * @throws IOException
 */
private static BufferedImage scale(String srcImageFile, int width, int height, boolean hasFiller) throws IOException {
	double ratio = 1.0; // 缩放比例
	File file = new File(srcImageFile);
	BufferedImage srcImage = ImageIO.read(file);
	Image destImage = srcImage.getScaledInstance(width, height, BufferedImage.SCALE_SMOOTH);
	// 计算比例
	if ((srcImage.getHeight() != height) || (srcImage.getWidth() != width)) {
		if (srcImage.getHeight() > srcImage.getWidth()) {
			ratio = (new Integer(height)).doubleValue() / srcImage.getHeight();
		} else {
			ratio = (new Integer(width)).doubleValue() / srcImage.getWidth();
		}
		//等比例缩放。若不等比例缩放,而是直接缩放,这需要两个缩放因子ratiox和ratioy分别传入即可
		AffineTransformOp op = new AffineTransformOp(AffineTransform.getScaleInstance(ratio, ratio), null);
		destImage = op.filter(srcImage, null);
	}
	if (hasFiller) {// 补白
		BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
		Graphics2D graphic = image.createGraphics();
		graphic.setColor(Color.white);
		graphic.fillRect(0, 0, width, height);
		if (width == destImage.getWidth(null))
			graphic.drawImage(destImage, 0, (height - destImage.getHeight(null)) / 2, destImage.getWidth(null), destImage.getHeight(null), Color.white, null);
		else
			graphic.drawImage(destImage, (width - destImage.getWidth(null)) / 2, 0, destImage.getWidth(null), destImage.getHeight(null), Color.white, null);
		graphic.dispose();
		destImage = image;
	}
	return (BufferedImage) destImage;
}

整合二维码、logo图片、边框并加上颜色。

public void testEncode2() throws WriterException, IOException {
	//**个人见解:logo高宽为二维码高宽1/5为佳,logo边框宽度为二维码高宽1/10为佳。logo占比例太大会导致难识别
	int logoW = 20; //logo图像宽度
	int logoH = 20; //logo图像高度
	int width = 200; // 二维码图像宽度
	int height = 200; // 二维码图像高度
	int frameW = 2; //边框宽度
	String logoUrl = "D:/Koala.jpg"; //logo图片地址

	BufferedImage scaledImage = scale(logoUrl, logoW, logoH, false);
	//声明二维数组,用于存储logo像素RGB值
	int[][] scaledPixels = new int[scaledImage.getWidth()][scaledImage.getHeight()];
	//将缩放后的图片像素点RGB值存放到二维数组中
	for (int i = 0; i < scaledImage.getWidth(); i++) {
		for (int j = 0; j < scaledImage.getHeight(); j++) {
			scaledPixels[i][j] = scaledImage.getRGB(i, j);
		}
	}
	String content = "内容";//这里content为二维码数据。为地址将直接访问,为text将直接解析
	String format = "jpg";// 图像类型
	Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>();
	hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
	BitMatrix matrix = new MultiFormatWriter().encode(content,BarcodeFormat.QR_CODE, width, height, hints);// 生成矩阵
	//声明并计算赋值二维码矩阵和中心图的半宽和半高,用于判断计算各部分区域
	int matrixHalfWidth = matrix.getWidth() / 2;
	int matrixHalfHeight = matrix.getHeight() / 2;
	int logoHalfWidth = scaledImage.getWidth() / 2;
	int logoHalfHeight = scaledImage.getHeight() / 2;
	//声明一维数组
	int[] pixels = new int[matrix.getWidth() * matrix.getHeight()];
	for (int y = 0; y < matrix.getHeight(); y++) {
		for (int x = 0; x < matrix.getWidth(); x++) {
			//中心图片
			if(x > matrixHalfWidth - logoHalfWidth
					&& x < matrixHalfWidth + logoHalfWidth
					&& y > matrixHalfHeight - logoHalfHeight
					&& y <  matrixHalfHeight + logoHalfHeight){
				pixels[y * width + x] = scaledPixels[x - matrixHalfWidth + logoHalfWidth][y - matrixHalfHeight + logoHalfHeight];
			//边框
			}else if((x > matrixHalfWidth - logoHalfWidth - frameW
					&& x < matrixHalfHeight + logoHalfHeight + frameW
					&& y > matrixHalfHeight - logoHalfHeight - frameW
					&& y < matrixHalfHeight + logoHalfHeight + frameW)
					&& !(x > matrixHalfWidth - logoHalfWidth
							&& x < matrixHalfWidth + logoHalfWidth
							&& y > matrixHalfHeight - logoHalfHeight
							&& y <  matrixHalfHeight + logoHalfHeight)){
				pixels[y * width + x] = 0xfffffff;//白色
			}else{
				//这里是二维码颜色,可以设置二维码和背景颜色,方法get(x, y)获取此像素点上是否存在矩阵点
				pixels[y * width + x] = matrix.get(x, y) ? 0xff000000 : 0xfffffff;
			}
		}
	}
	BufferedImage image = new BufferedImage(matrix.getWidth(), matrix.getHeight(), BufferedImage.TYPE_INT_RGB);
	image.getRaster().setDataElements(0, 0, matrix.getWidth(), matrix.getHeight(), pixels);
	ImageIO.write(image, format, new File("D:/qrcode.png"));
}

这样就完成了带logo的二维码设计。


二维码解码
下面对二维码进行解码,解码代码如下:

@Test
public void testDecode(){
	try {
		String filePath = "D:/qrcode_test.png";
		// 读取二维码图片到缓冲区
		BufferedImage image = ImageIO.read(new File(filePath));
		LuminanceSource source = new BufferedImageLuminanceSource(image);
		Binarizer binarizer = new HybridBinarizer(source);
		BinaryBitmap binaryBitmap = new BinaryBitmap(binarizer);
		Map<DecodeHintType, Object> hints = new HashMap<DecodeHintType, Object>();
		hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
		Result result = new MultiFormatReader().decode(binaryBitmap, hints);// 对图像进行解码
		System.out.println("======>>encode Type: " + result.getBarcodeFormat() + "      ======>>content:" + result.getText());
	} catch (Exception e) {
		e.printStackTrace();
	}
}

支持带logo和未带logo解码。
 

相关文章:

  • 如何在手机浏览器中打开安卓APP
  • Postgresql 解决pg掉电后无法重启的问题
  • Git下载安装vue-antd-admin教程
  • dubbo服务暴露流程
  • 人老了才明白,走到最后,身边能依靠的不是老伴,也不是子女
  • PySpark SQL模块官方文档
  • 独立三方数据源!数据宝国有大数据为金融机构及物流企业提供双向赋能
  • Vuehtml2pdf的使用
  • java计算机毕业设计图书馆管理系统源码+系统+数据库+lw文档+mybatis+运行部署
  • 南京国家农创聚功能性农业主导产业 国稻种芯百团计划行动
  • A. Balance the Bits (思维构造)
  • k8s系列(二)——云计算相关概念
  • 数据挖掘学习笔记01——数据挖掘的基本流程
  • 分布式缓存Hazelcast的部署及与SpringBoot整合使用
  • 1.5 Elasticsearch文档的基本操作
  • 《深入 React 技术栈》
  • Android Volley源码解析
  • ESLint简单操作
  • Fastjson的基本使用方法大全
  • Hexo+码云+git快速搭建免费的静态Blog
  • iOS编译提示和导航提示
  • Java 最常见的 200+ 面试题:面试必备
  • Javascript编码规范
  • Leetcode 27 Remove Element
  • Linux快速配置 VIM 实现语法高亮 补全 缩进等功能
  • MySQL QA
  • Python 使用 Tornado 框架实现 WebHook 自动部署 Git 项目
  • Spark学习笔记之相关记录
  • vue从创建到完整的饿了么(18)购物车详细信息的展示与删除
  • windows下mongoDB的环境配置
  • 阿里云ubuntu14.04 Nginx反向代理Nodejs
  • 动态规划入门(以爬楼梯为例)
  • 飞驰在Mesos的涡轮引擎上
  • 工作中总结前端开发流程--vue项目
  • 构建工具 - 收藏集 - 掘金
  • 基于Mobx的多页面小程序的全局共享状态管理实践
  • 看完九篇字体系列的文章,你还觉得我是在说字体?
  • 如何用vue打造一个移动端音乐播放器
  • 什么是Javascript函数节流?
  • 世界编程语言排行榜2008年06月(ActionScript 挺进20强)
  • 微信端页面使用-webkit-box和绝对定位时,元素上移的问题
  • 小试R空间处理新库sf
  • 写代码的正确姿势
  • 移动端解决方案学习记录
  • python最赚钱的4个方向,你最心动的是哪个?
  • 移动端高清、多屏适配方案
  • #### go map 底层结构 ####
  • #define用法
  • #ifdef 的技巧用法
  • #QT(TCP网络编程-服务端)
  • #传输# #传输数据判断#
  • (Redis使用系列) SpirngBoot中关于Redis的值的各种方式的存储与取出 三
  • (提供数据集下载)基于大语言模型LangChain与ChatGLM3-6B本地知识库调优:数据集优化、参数调整、Prompt提示词优化实战
  • ***检测工具之RKHunter AIDE
  • ./mysql.server: 没有那个文件或目录_Linux下安装MySQL出现“ls: /var/lib/mysql/*.pid: 没有那个文件或目录”...