Java生成二维码及条形码工具
一:前言
二维码是一种可以存储信息的矩形图案,它可以在移动设备上进行扫描和读取信息。Java语言中有许多库可以用于生成和解码二维码,其中com.google.zxing是一种常用的库。com.google.zxing是一个开源的Java库,它可以用于生成和解码各种类型的二维码和条形码。这个库的优点是易于使用,具有很高的扫描成功率和在各种不同设备上的兼容性。
1:概述及导入
Java 操作二维码的开源项目很多,如 SwetakeQRCode、BarCode4j、Zxing 等等;但本文将介绍简单易用的 google 公司的 zxing,它使用方便,可以操作条形码或者二维码,并且还可以对其进行解析,下面将依次介绍。
因为是Maven项目,这里我就直接导入pom坐标,下面详细介绍。
若想直接上手则看最后一节(这个重要哟,一个我按照前面的章节封装的小巧工具类)
<!-- Google开发工具包 Start--> <!-- core包提供了条形码和二维码的生成和解码接口 --> <!-- javaSE包方便Java SE应用集成zxing而提供的包,提供了使用Swing和AWT应用程序的API --> <dependency> <groupId>com.google.zxing</groupId> <artifactId>core</artifactId> <version>3.5.1</version> </dependency> <dependency> <groupId>com.google.zxing</groupId> <artifactId>javase</artifactId> <version>3.5.1</version> </dependency> <!-- Google开发工具包 END-->
com.google.zxing库中的core包和javase包是zxing库的两个核心包,它们都包含了二维码的生成和解码相关的代码,但它们的功能和使用范围存在一定的差异。core包是zxing库中最基本和核心的包,主要提供了二维码生成和解码的基本算法和接口,是其它zxing库的基础,可以理解为是整个zxing库的“核心”。而javase包则是zxing库中的一个扩展包,以javax.imageio API为基础,提供了在Java SE环境中生成、解码QR码的解决方案。它包含了更高层次的API,可以快速、方便地在Java SE环境中完成二维码的生成和解码操作。因此,如果需要在Java SE环境中使用zxing库的生成和解码功能,建议直接使用javase包进行开发。这里我就一次性都导入了。
二:一个最基本的案例
1:基础介绍
这小节只是一些类的介绍;在后面的学习中遇到不明白的类可以在这查询
EncodeHintType枚举类: 指定和控制二维码生成器的一些参数的类。可以为二维码生成器提供提示,以便控制生成的二维码的各种属性和设置。 CHARACTER_SET: 指定生成二维码所采用的字符集编码。字符集指定了二维码中存储的文本的字符编码方式。默认情况下是UTF-8。 ERROR_CORRECTION: 二维码所要采用的纠错级别。纠错级别是指在生成二维码时预留的纠错码容量,以及在扫描过程中的二维码损坏程度与还原的可靠程度。 常量有L、M、Q和H四个级别,级别越高,二维码就有更强的纠错能力,但也会占用更多的二维码面积。 MARGIN: 指定二维码图像的外边距大小。外边距可以让二维码更加易于扫描读取。 MIN_SIZE: 指定二维码的最小尺寸。可以使用Dimension类型指定宽度和高度。 MAX_SIZE: 指定二维码的最大尺寸。可以使用Dimension类型指定宽度和高度。 DATA_MATRIX_SHAPE: 这个常量用于指定Data Matrix码的形状。常量有FORCE_NONE、FORCE_SQUARE和FORCE_RECTANGLE三个取值。它们分别表示 不强制规格、规格为正方形和规格为矩形。 DATA_MATRIX_COMPACT: 用于设置Data Matrix码是否采用紧凑布局的编码提示类型。紧凑布局是一种更加紧凑的Data Matrix码布局方式,可以使生成的码 更小,但是存储的信息量也会相应减少。该编码提示类型可以被设置为Boolean类型,以控制是否使用紧凑布局。默认情况下,如果未设 置该编码提示类型,则Data Matrix码将使用正常的布局方式。 QR_VERSION: QR码的版本号决定了二维码中所能包含的信息量,版本号越高,二维码的容量就越大,能包含的信息量也就越多。例如,版本1的QR码 只能容纳最多25个字符,而版本40的QR码则可以容纳最多7089个字符。如果不指定版本号,它会自动选择一个版本号来满足所需的信息量。 QR_MASK_PATTERN: 在QR码中,掩模用于调整编码的数据模块的颜色和位置,以提高二维码的可读性。QrMaskPattern枚举类型包含了八种掩模类型可供选择。 默认情况下,代码将自动选择最佳掩模图案 QR_COMPACT: 设置QR码是否采用紧凑式布局的编码提示类型。是一种更加紧凑的QR码布局方式,可使生成的QR码更小,但存储的信息量也会相应减少。 ....
BarcodeFormat枚举类(用于表示条形码格式的类;常用QR_CODE二维码格式;EAN_13条形码格式): 用于表示条形码格式的类,其中包含了常见的条形码格式,如UPC-A、Code 128、Data Matrix等。 AZTEC: 是二维码格式之一,可用于编码数字、字母、标点符号、二进制数据及全字节字符集等信息。 它可以存储相当于 3000 个符号的信息,同时拥有纠错能力。 CODABAR: 基于数字和字符的条形码格式,在邮局、图书馆、医院等场合经常被应用。它能够编码数字 0-9、字符 $、:、/、+ 和 -。 CODE_39: 一种字符条形码格式,它使用 43 种字符来编码字母、数字和几个特殊字符。它也支持从 1 到 9 的长度可变编码。 CODE_93: 一种可变长度、双向编码的高密度条形码格式。它支持128个字符,包括字母、数字和一些特殊字符。 CODE_128: 一种高密度、可变长度、双向编码的条形码格式。它支持包含数字、字母、标点符号和其他非可视字符的所有 ASCII 字符。 DATA_MATRIX: 一种高密度二维码格式,可以在小空间内存储大量信息。它支持编码任何 8 位代码,包括 ASCII 码、二进制数、数值等。 EAN_8: 一种用于小型产品的 8 位数字条形码格式。它可以存储不超过 8 个数字。 EAN_13: 一种标准的 13 位数字条形码格式,通常用于商品的包装。它可以存储比 EAN-8 更多的信息。 ITF: 一种数字条形码格式,由数字 0-9 组成。它可以很好地应用于库存控制和配送等各种领域。 MAXICODE: 一种二维码格式,通常在物流和包裹追踪领域应用。它最高可存储 93 个字符,包括数字、字母、标点符号和控制字符。 PDF_417: 一种二维条码格式,最初用于美国公路交通管理机构的许可证。它可以存储比普通的条形码格式更多的信息。 QR_CODE: 一种广泛使用的二维码格式,在各种应用场合中都有使用。它可以在较小的空间内存储大量信息,同时拥有纠错能力和重建数据能力。 RSS_14: 一种可变长度、高密度条形码格式,支持多种数据类型。它适用于物流和库存控制等领域。 RSS_EXPANDED: 是RSS 14的扩展格式,支持多种数据类型。它可以存储更多的信息并更容易在物流和库存控制等领域应用。 UPC_A: 一种标准的 12 位数字条形码格式,通常用于商品的包装和销售。 UPC_E: 一种压缩格式的 UPC 标准,只用 6 位数字来编码商品号。它在小空间内存储信息,通常用于小型物品。 UPC_EAN_EXTENSION: UPC/EAN 扩展格式是一种可选格式,可以用于同时编码 UPC 和 EAN 码。它可以向条形码中添加额外的信息, 如批次号、生产日期、包装袋重量等信息。
补充A:二维码的容错能力等级(EncodeHintType枚举类里的ERROR_CORRECTION)
二维码容错率用字母表示,容错能力等级分为:L、M、Q、H四级,当二维码图片被遮挡一部分后,仍可以扫描出来。
容错的原理是二维码在编码过程中对内容进行了冗余,就像内容是"Hello"被编码成"HelloHello",这样只要扫描到一部分二维码图片,
二维码内容还是可以被全部读到。二维码容错率即是指二维码图标被遮挡多少后,仍可以被扫描出来的能力。
但是容错率越高,则二维码图片能被遮挡的部分越多。
L:低,最大 7% 的错误能够被纠正
M:中,最大 15% 的错误能够被纠正
Q:中上,最大 25% 的错误能够被纠正
H:高,最大 30% 的错误能够被纠正
2:生成二维码
这里将在main方法中生成一个简单信息的二维码,然后生成出来的二维码PNG图片信息通过流的方式写到当前项目目录下,其中输出二维码时我们可以在二维码中指定一个logo图标。
import com.google.zxing.BarcodeFormat; import com.google.zxing.EncodeHintType; import com.google.zxing.MultiFormatWriter; import com.google.zxing.WriterException; import com.google.zxing.common.BitMatrix; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import javax.imageio.ImageIO; import java.awt.*; import java.awt.geom.RoundRectangle2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.Date; import java.util.HashMap; import java.util.Objects; /** * 一个简单的二维码生成 * * @author Anhui OuYang * @version 1.0 **/ public class GenerateQRCode { private static final Integer CODE_WIDTH = 300; // 基础属性:二维码宽度,单位像素 private static final Integer CODE_HEIGHT = 300; // 基础属性:二维码高度,单位像素 private static final Integer FRONT_COLOR = 0x000000; // 基础属性:二维码前景色,0x000000 表示黑色 private static final Integer BACKGROUND_COLOR = 0xFFFFFF; // 基础属性:二维码背景色,0xFFFFFF 表示白色 public static void main(String[] args) throws WriterException, IOException { //获取当前项目的路径根目录 String path = Objects.requireNonNull(GenerateQRCode.class.getResource("/")).getPath(); //生成文件名及文件的路径 File filePathAndName = new File(path, new Date().getTime() + ".png"); //设置生成二维码的内容 String qrMessage = "https://www.baidu.com"; // EncodeHintType:编码提示类型,枚举类型 // EncodeHintType.CHARACTER_SET:设置字符编码类型 // EncodeHintType.ERROR_CORRECTION:设置误差校正 // ErrorCorrectionLevel:误差校正等级, // L = ~7% correction、M = ~15% correction、 // Q = ~25% correction、H = ~30% correction // 不设置时,默认为 L 等级,等级不一样,生成的图案不同,但扫描的结果是一样的 // EncodeHintType.MARGIN:设置二维码边距,单位像素,值越小,二维码距离四周越近 HashMap<EncodeHintType, Object> typeObjectHashMap = new HashMap<>(); typeObjectHashMap.put(EncodeHintType.CHARACTER_SET, "UTF-8"); typeObjectHashMap.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M); typeObjectHashMap.put(EncodeHintType.MARGIN, 1); // MultiFormatWriter:多格式写入,这是一个工厂类,里面重载了两个 encode 方法,用于写入条形码或二维码 // encode(String contents,BarcodeFormat format,int width, int height,Map<EncodeHintType,?> hints) // 参数1:contents:条形码/二维码内容 // 参数2:format:编码类型,如 条形码,二维码 等 // 参数3:width:码的宽度 // 参数4:height:码的高度 // 参数5:hints:码内容的编码类型 // BarcodeFormat:枚举该程序包已知的条形码格式,即创建何种码,如 1 维的条形码,2 维的二维码 等 // BitMatrix:位(比特)矩阵或叫2D矩阵,也就是需要的二维码 MultiFormatWriter multiFormatWriter = new MultiFormatWriter(); BitMatrix bitMatrix = multiFormatWriter .encode(qrMessage, BarcodeFormat.QR_CODE, CODE_WIDTH, CODE_HEIGHT, typeObjectHashMap); // java.awt.image.BufferedImage:具有图像数据的可访问缓冲图像,实现了 RenderedImage 接口 // BitMatrix 的 get(int x, int y) 获取比特矩阵内容,指定位置有值,则返回true,将其设置为前景色,否则设置为背景色 // BufferedImage 的 setRGB(int x, int y, int rgb) 方法设置图像像素 // 参数1 x:像素位置的横坐标,即列 // 参数2 y:像素位置的纵坐标,即行 // 参数3 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); } } // 把logo图片设置到二维码中间位置(若不想在二维码中间插入logo图片则不执行此条代码) insertLogoImage(bufferedImage, GenerateQRCode.class.getResourceAsStream("/logo.png")); // javax.imageio.ImageIO java扩展的图像IO // write(RenderedImage im,String formatName,File output) // im:待写入的图像 // formatName:图像写入的格式 // output:写入的图像文件,文件不存在时会自动创建 // 即将保存的二维码图片文件 ImageIO.write(bufferedImage, "png", filePathAndName); System.out.println("当前写入图片在:" + filePathAndName.getPath()); } /*** * 为二维码插入中间的logo图片 * @param bufferedImage 二维码图片信息 * @param logoInput logo图片位置 * @throws IOException 抛出IO异常 */ private static void insertLogoImage(BufferedImage bufferedImage, InputStream logoInput) throws IOException { // 加载Logo图片到Image对象 Image logoImage = ImageIO.read(logoInput); // 设置二维码中间的Logo图片(设置logo图片的宽高需要保持和二维码宽高的一半宽高) int logoWidth = CODE_WIDTH / 5; int logoHeight = CODE_HEIGHT / 5; // 按照指定的宽高进行等比缩放(若设置的宽高比之前大则代表放大),并将缩放后的图片绘制到一个新的BufferedImage对象中 Image image = logoImage.getScaledInstance(logoWidth, logoHeight, Image.SCALE_SMOOTH); BufferedImage logoBufferedImage = new BufferedImage(logoWidth, logoHeight, BufferedImage.TYPE_INT_RGB); // 获取图形上下文对象,用于后续将缩放后的图片绘制在 logoBufferedImage 对象上 Graphics graphics = logoBufferedImage.getGraphics(); // 绘制缩放后的图片到 logoBufferedImage 对象上,使其填满整个画布 graphics.drawImage(image, 0, 0, null); // 释放图形上下文对象,避免内存泄漏 graphics.dispose(); // 把处理好的logo图片再次设置给之前的logo图片对象 logoImage = image; // 开始把logo图片插入到二维码的中间位置 // 获取 Graphics2D 对象,用于后续在 bufferedImage 上绘制二维码和 logo 图片 Graphics2D graph = bufferedImage.createGraphics(); // 计算出 logo 图片在二维码中间的坐标点 (x, y),以便后续将 logo 图片插入到正确的位置。 int x = (CODE_WIDTH - logoWidth) / 2; int y = (CODE_HEIGHT - logoHeight) / 2; // 绘制缩放后的 logo 图片到二维码中间位置 graph.drawImage(logoImage, x, y, logoWidth, logoHeight, null); // 设置边框的线条粗细为 3 像素,并且设置线条是一个圆角矩形,6表示圆角的半径。并关闭资源 graph.setStroke(new BasicStroke(3f)); graph.draw(new RoundRectangle2D.Float(x, y, logoWidth, logoHeight, 6, 6)); graph.dispose(); } }
生成几个常用的二维码,都是使用QR_CODE类型的二维码,其实还有生成带logo图标的二维码,但是注意的是,logo图标的宽高一定是二维码四边的一半宽高
3:生成条形码
这里的条形码生成和上面的代码基本上一样,但生成的码类型为EAN_13条形码格式,注意条形码的生成数据必须按照规范
/** * 一个简单的条形码生成 * * @author Anhui OuYang * @version 1.0 **/ public class GenerateBarcode { private static final Integer CODE_WIDTH = 300; // 基础属性:二维码宽度,单位像素 private static final Integer CODE_HEIGHT = 150; // 基础属性:二维码高度,单位像素 private static final Integer FRONT_COLOR = 0x000000; // 基础属性:二维码前景色,0x000000 表示黑色 private static final Integer BACKGROUND_COLOR = 0xFFFFFF; // 基础属性:二维码背景色,0xFFFFFF 表示白色 public static void main(String[] args) throws WriterException, IOException { //获取当前项目的路径根目录 String path = Objects.requireNonNull(GenerateQRCode.class.getResource("/")).getPath(); //生成文件名及文件的路径 File filePathAndName = new File(path, new Date().getTime() + ".png"); //设置生成条形码的内容(注:条形码信息一定要按照格式,如随便一个商品上的条形码都行,但必须是EAN_13格式) String qrMessage = "6971483746515"; // EncodeHintType:指定和控制码生成器的一些参数的类 HashMap<EncodeHintType, Object> typeObjectHashMap = new HashMap<>(); typeObjectHashMap.put(EncodeHintType.CHARACTER_SET, "UTF-8"); typeObjectHashMap.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); typeObjectHashMap.put(EncodeHintType.MARGIN, 1); // MultiFormatWriter:用于写入条形码或二维码 MultiFormatWriter multiFormatWriter = new MultiFormatWriter(); BitMatrix bitMatrix = multiFormatWriter .encode(qrMessage, BarcodeFormat.EAN_13, CODE_WIDTH, CODE_HEIGHT, typeObjectHashMap); // java.awt.image.BufferedImage:把条形码信息转换为图像信息 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); } } // javax.imageio.ImageIO 将图片信息写出到文件 ImageIO.write(bufferedImage, "png", filePathAndName); System.out.println("当前写入图片在:" + filePathAndName.getPath()); } }
这里生成条形码样式就有点丑了,但是对应日常开发,我们掌握二维码就可以了,针对条形码我们需要有专业的条形码API操作
4:二维码解析
其实二维码的解析挺简单的,具体代码如下,但是条形码却不那么简单,因为涉及的方面太多了。
生成条形码可能相对较为简单,因为在生成过程中,我们只需要按照一定规则将数据序列转换成条形码即可。而在解析条形码时,需要对条形码进行扫描,并对扫描得到的数据进行处理,才能还原出原始的数据序列。具体来说,在生成条形码时,我们可以根据各种不同的规则和算法,将一串数字、文本或二进制数据转换成条形码。例如,在 EAN-13 条形码中,我们需要按照规定的方式计算出校验码,再将前缀和校验码添加到数据中,最终就可以生成完整的 EAN-13 条形码。但是在解析条形码时,我们需要从条形码中读取和识别信息,并将其转换成计算机可读的数据,这就需要使用光学字符识别等技术对条形码进行扫描和解析,以提取出其中的数据信息。同时,由于条形码具有很高的容错性,所以在解析过程中还需要进行多次尝试和修正,以保证解析的准确性和可靠性。因此,尽管在生成条形码时可能看起来相对简单,但是解析条形码却要涉及到多个领域的知识和技术,因此会显得更加复杂和困难。所以这里就不处理条形码的解析了。
/** * 二维码解析 * * @author Anhui OuYang * @version 1.0 **/ public class QRCodeParsing { public static void main(String[] args) throws IOException, NotFoundException { System.out.println("=========================二维码解析 Start========================="); //获取当前项目的路径根目录 String path = Objects.requireNonNull(GenerateQRCode.class.getResource("/")).getPath(); //获取路径下的文件 File filePathAndName = new File(path, "1684305639072.png"); parsingQRCode(filePathAndName); System.out.println("=========================二维码解析 End========================="); // 打印信息 // =========================二维码解析 Start========================= // 二维码解析图片的路径: D:\JavaObject\SwaggerDemo\target\classes\1684305639072.png // 二维码解析图片中格式: QR_CODE // 二维码解析图片中内容: https://www.baidu.com // =========================二维码解析 End========================= } /*** * 二维码(QR_CODE)解析 * @param file 文件路径信息 */ public static void parsingQRCode(File file) throws IOException, NotFoundException { //创建一个图像数据缓冲区类(并读取二维码图片) BufferedImage bufferedImage = ImageIO.read(file); // 对图片信息的图像处理和分析 // 注意需要 zxing 库的版本 3.2.1 或更高版本才能使用,若没有则使用我下面定义的getRGBPixelData(BufferedImage image) BufferedImageLuminanceSource luminanceSource = new BufferedImageLuminanceSource(bufferedImage); // 若版本不够则使用这种方式 // RGBLuminanceSource luminanceSource = new RGBLuminanceSource(bufferedImage.getWidth(), // bufferedImage.getHeight(), getRGBPixelData(bufferedImage)); // 创建BinaryBitmap对象 // BinaryBitmap是二值化位图,将像素从RGB空间转换为黑白颜色空间 BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(luminanceSource)); // 设置解码参数 Map<DecodeHintType, Object> hints = new HashMap<>(); hints.put(DecodeHintType.CHARACTER_SET, "UTF-8"); // 设置解析的字符集 // 使用 MultiFormatReader 进行解码 MultiFormatReader formatReader = new MultiFormatReader(); formatReader.setHints(hints); // 设置解码一些参数 Result result = formatReader.decode(binaryBitmap); // 解码(解析binaryBitmap数据) // 打印一些信息 System.out.println("二维码解析图片的路径: " + file.getPath()); System.out.println("二维码解析图片中格式: " + result.getBarcodeFormat()); System.out.println("二维码解析图片中内容: " + result.getText()); } /*** * 将 BufferedImage 转为 RGB 像素数组 * @param image 图像数据缓冲区类信息 * @return 返回RGB像素数组 */ private static int[] getRGBPixelData(BufferedImage image) { // 获取 BufferedImage 对象的宽度和高度 int width = image.getWidth(); int height = image.getHeight(); // 计算 RGB 像素数组的长度,并创建数组 int[] pixels = new int[width * height]; // 获取 BufferedImage 对象的像素数据,并将其存储到 RGB 像素数组中 image.getRGB(0, 0, width, height, pixels, 0, width); // 返回 RGB 像素数组 return pixels; } }
三:在Web中使用及整理工具包
其实我们使用二维码或者条形码,大部分都是在JavaWeb项目中使用,我们可以生成流的方式输出给前端或者使用图片请求的方式等等,但是最终我们需要调用工具包来生成二维码或者条形码输出给前端,这里我将使用流的方式或者图片请求的方式输出。
1:封装一个条形码或二维码的工具包
具体的代码我上传到Gitee上的QRCodeBarcodeWebDemo项目上,下面简单介绍使用方式:
使用说明:
二维码生成工具是对 google.zxing 的包进行的一下封装,提供了二维码和条形码的生成,以及二维码的解析
方法说明:
String decodeQRCode(File codeFile)
String decodeQRCode(InputStream codeInput)
Result decodeQRCode(BufferedImage bufferedImage)
说明:上面三个方法是用来解析二维码的,注意的是只能传File、InputStream、BufferedImage三种类型
void encodeQRCode(String content, HttpServletResponse response)
void encodeQRCode(String content, String logoPath, HttpServletResponse response)
void encodeQRCode(String content, File logoFile, HttpServletResponse response)
void encodeQRCode(String content, InputStream logoInput, HttpServletResponse response)
说明:上面四个方法是用来生成二维码的,
参数1:生成二维码的信息
参数2:生成二维码中间的Logo图标信息
参数3:用来写出到前端页面的响应对象
注意:第一个方法有2个参数,它没生成的是不带Logo图标的
void encodeBarcode(String content, HttpServletResponse response)
说明:这个方法用来生成条形码并响应到前端
void setResponseHeaderInfoDownload(HttpServletResponse response)
注意:这个是设置响应头信息的,若调用这个方法则代表当前响应到前端的二维码以下载的方式下载图片
配置说明:
其实一般我们使用默认的配置就可以了,但是我们要调整二维码的生成属性则使用当前这个类,其中有几个重要配置
errorCorrection:容错等级,若创建带Logo图标的二维码我们一般选择M、Q、H
checkQRCode:生成的二维码是否需要校验其完整性,若校验失败可以调整宽高
封装的二维码工具是我当前使用的,若在实际使用遇到问题我也会进行更新。