使用zxing工具包创建和解析二维码

简介:

关于二维码是什么,以及二维码是如何生成的,我也没有研究得很深入,就不多说了,以免误导大家。请参看:

下面是一个可以生成和解析二维码的工具类,该类用到了zxing工具包,我通过Maven去下载的:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
< dependencies >
     <!-- JavaSE包依赖于Core包,因此Core包不需要直接依赖了
     <dependency>
         <groupId>com.google.zxing</groupId>
         <artifactId>core</artifactId>
         <version>3.1.0</version>
     </dependency> -->
     < dependency >
         < groupId >com.google.zxing</ groupId >
         < artifactId >javase</ artifactId >
         < version >3.1.0</ version >
     </ dependency >
</ dependencies >

在网上搜索的时候我发现,有不少同学在使用maven的时候都同时导入了这两个包,但是我发现这个artifactId为javase的包依赖于core包,因此我们不需要再在pom.xml中声明对core包的依赖了。

164131_aaJJ_1434710.png

下面这个类是一个工具类,该类可以生成一维码和二维码,也可以解析二维码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
package  com.abc.qrcode;
 
import  java.awt.Color;
import  java.awt.Graphics2D;
import  java.awt.Image;
import  java.awt.geom.AffineTransform;
import  java.awt.image.AffineTransformOp;
import  java.awt.image.BufferedImage;
import  java.io.ByteArrayInputStream;
import  java.io.ByteArrayOutputStream;
import  java.io.File;
import  java.io.IOException;
 
import  javax.imageio.ImageIO;
 
import  com.google.zxing.BarcodeFormat;
import  com.google.zxing.Binarizer;
import  com.google.zxing.BinaryBitmap;
import  com.google.zxing.LuminanceSource;
import  com.google.zxing.MultiFormatReader;
import  com.google.zxing.MultiFormatWriter;
import  com.google.zxing.NotFoundException;
import  com.google.zxing.ReaderException;
import  com.google.zxing.Result;
import  com.google.zxing.WriterException;
import  com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import  com.google.zxing.common.BitMatrix;
import  com.google.zxing.common.HybridBinarizer;
 
public  class  QRCodeUtil {
     // 这几项可以由其他调用的类设置,因此是public static的
     public  static  int  BARCODE_WIDTH =  80 ;
     public  static  int  QRCODE_WIDTH =  200 ;
     public  static  String FORMAT =  "jpg" ; // 生成的图片格式
     public  static  int  BLACK =  0x000000 ; // 编码的颜色
     public  static  int  WHITE =  0xFFFFFF ; // 空白的颜色
 
     // 二维码中间的图像配置。注意,由于二维码的容错率有限,因此中间遮挡的面积不要太大,否则可能解析不出来。
     private  static  int  ICON_WIDTH = ( int )(QRCODE_WIDTH /  6 );
     private  static  int  HALF_ICON_WIDTH = ICON_WIDTH /  2 ;
     private  static  int  FRAME_WIDTH =  2 ; // Icon四周的边框宽度
     
     // 二维码读码器和写码器
     private  static  final  MultiFormatWriter WRITER =  new  MultiFormatWriter();
     private  static  final  MultiFormatReader READER =  new  MultiFormatReader();
 
     // 测试
     public  static  void  main(String[] args)  throws  Exception {
         /**
          * 二维码测试。
          */
         String iconPath =  "C:\\icon.jpg" ;
         String content =  "http://www.baidu.com" ;
         File qrCode =  new  File( "C:\\QRCode."  + FORMAT);
         File qrCodeWithIcon =  new  File( "C:\\QRCodeWithIcon."  + FORMAT);
         // 生成二维码
         writeToFile(createQRCode(content), qrCode);
         // 生成带图标的二维码
         writeToFile(createQRCodeWithIcon(content, iconPath), qrCodeWithIcon);
         // 解析二维码
         System.out.println(parseImage(qrCode));
         // 解析带图标的二维码
         System.out.println(parseImage(qrCodeWithIcon));
 
         // 编码成字节数组
         byte [] data = createQRCodeToBytes(content);
         String result = parseQRFromBytes(data);
         System.out.println(result);
 
         /**
          * 一维码测试。
          */
         String barCodeContent= "6936983800013" ;
         File barCode =  new  File( "C:\\BarCode."  + FORMAT);
         // 生成一维码
         writeToFile(createBarCode(barCodeContent), barCode);
         // 解析一维码
         System.out.println(parseImage(barCode));
     }
     
 
     /**
      * 将String编码成二维码的图片后,使用字节数组表示,便于传输。
     
      * @param content
      * @return
      * @throws WriterException
      * @throws IOException
      */
     public  static  byte [] createQRCodeToBytes(String content) 
             throws  WriterException, IOException {
         BufferedImage image = createQRCode(content);
         ByteArrayOutputStream os =  new  ByteArrayOutputStream();
         ImageIO.write(image, FORMAT, os);
         return  os.toByteArray();
     }
 
     /**
      * 把一个String编码成二维码的BufferedImage.
     
      * @param content
      * @return
      * @throws WriterException
      */
     public  static  final  BufferedImage createQRCode(String content) 
             throws  WriterException {
         // 长和宽一样,所以只需要定义一个SIZE即可
         BitMatrix matrix = WRITER.encode(
                 content, BarcodeFormat.QR_CODE, QRCODE_WIDTH, QRCODE_WIDTH);
         return  toBufferedImage(matrix);
     }
     
     /**
      * 编码字符串为二维码,并在该二维码中央插入指定的图标。
      * @param content
      * @param iconPath
      * @return
      * @throws WriterException
      */
     public  static  final  BufferedImage createQRCodeWithIcon(
             String content, String iconPath)  throws  WriterException {
         BitMatrix matrix = WRITER.encode(
                 content, BarcodeFormat.QR_CODE, QRCODE_WIDTH, QRCODE_WIDTH);
         // 读取Icon图像
         BufferedImage scaleImage =  null ;
         try  {
             scaleImage = scaleImage(iconPath, ICON_WIDTH, ICON_WIDTH,  true );
         catch  (IOException e) {
             e.printStackTrace();
         }
         int [][] iconPixels =  new  int [ICON_WIDTH][ICON_WIDTH];
         for  ( int  i =  0 ; i < scaleImage.getWidth(); i++) {
             for  ( int  j =  0 ; j < scaleImage.getHeight(); j++) {
                 iconPixels[i][j] = scaleImage.getRGB(i, j);
             }
         }
 
         // 二维码的宽和高
         int  halfW = matrix.getWidth() /  2 ;
         int  halfH = matrix.getHeight() /  2 ;
         
         // 计算图标的边界:
         int  minX = halfW - HALF_ICON_WIDTH; //左
         int  maxX = halfW + HALF_ICON_WIDTH; //右
         int  minY = halfH - HALF_ICON_WIDTH; //上
         int  maxY = halfH + HALF_ICON_WIDTH; //下
         
         int [] pixels =  new  int [QRCODE_WIDTH * QRCODE_WIDTH];
 
         // 修改二维码的字节信息,替换掉一部分为图标的内容。
         for  ( int  y =  0 ; y < matrix.getHeight(); y++) {
             for  ( int  x =  0 ; x < matrix.getWidth(); x++) {
                 // 如果点在图标的位置,用图标的内容替换掉二维码的内容
                 if  (x > minX && x < maxX && y > minY && y < maxY) {
                     int  indexX = x - halfW + HALF_ICON_WIDTH;
                     int  indexY = y - halfH + HALF_ICON_WIDTH;
                     pixels[y * QRCODE_WIDTH + x] = iconPixels[indexX][indexY];
                 }
                 // 在图片四周形成边框
                 else  if  ((x > minX - FRAME_WIDTH && x < minX + FRAME_WIDTH 
                        && y > minY - FRAME_WIDTH && y < maxY + FRAME_WIDTH)
                       || (x > maxX - FRAME_WIDTH && x < maxX + FRAME_WIDTH 
                        && y > minY - FRAME_WIDTH && y < maxY + FRAME_WIDTH)
                       || (x > minX - FRAME_WIDTH && x < maxX + FRAME_WIDTH 
                        && y > minY - FRAME_WIDTH && y < minY + FRAME_WIDTH)
                       || (x > minX - FRAME_WIDTH && x < maxX + FRAME_WIDTH 
                        && y > maxY - FRAME_WIDTH && y < maxY + FRAME_WIDTH)) {
                     pixels[y * QRCODE_WIDTH + x] = WHITE;
                 }
                 else  {
                     // 这里是其他不属于图标的内容。即为二维码没有被图标遮盖的内容,用矩阵的值来显示颜色。
                     pixels[y * QRCODE_WIDTH + x] = matrix.get(x, y) ? BLACK : WHITE;
                 }
             }
         }
 
         // 用修改后的字节数组创建新的BufferedImage.
         BufferedImage image =  new  BufferedImage(
                 QRCODE_WIDTH, QRCODE_WIDTH, BufferedImage.TYPE_INT_RGB);
         image.getRaster().setDataElements( 0 0 , QRCODE_WIDTH, QRCODE_WIDTH, pixels);
 
         return  image;
     }
 
     /**
      * 从一个二维码图片的字节信息解码出二维码中的内容。
     
      * @param data
      * @return
      * @throws ReaderException
      * @throws IOException
      */
     public  static  String parseQRFromBytes( byte [] data) 
             throws  ReaderException, IOException {
         ByteArrayInputStream is =  new  ByteArrayInputStream(data);
         BufferedImage image = ImageIO.read(is);
         return  parseImage(image);
     }
 
     /**
      * 从一个图片文件中解码出二维码中的内容。
     
      * @param file
      * @return 解析后的内容。
      * @throws IOException
      * @throws ReaderException
      */
     public  static  final  String parseImage(File file) 
             throws  IOException, ReaderException {
         BufferedImage image = ImageIO.read(file);
         return  parseImage(image);
     }
 
     /**
      * 将字符串编码成一维码(条形码)。
      * @param content
      * @return
      * @throws WriterException
      * @throws IOException
      */
     public  static  BufferedImage createBarCode(String content) 
             throws  WriterException, IOException {
         MultiFormatWriter writer =  new  MultiFormatWriter();
         // 一维码的宽>高。这里我设置为 宽:高=2:1
         BitMatrix matrix = writer.encode(content, 
                 BarcodeFormat.EAN_13, BARCODE_WIDTH *  3 , BARCODE_WIDTH);
         return  toBufferedImage(matrix);
     }
 
     /**
      * 从图片中解析出一维码或者二维码的内容。如果解析失败,则抛出NotFoundException。
      * @param image
      * @return
      * @throws NotFoundException
      */
     public  static  final  String parseImage(BufferedImage image) 
             throws  NotFoundException {
         LuminanceSource source =  new  BufferedImageLuminanceSource(image);
         Binarizer binarizer =  new  HybridBinarizer(source);
         BinaryBitmap bitmap =  new  BinaryBitmap(binarizer);
         Result result = READER.decode(bitmap);
         // 这里丢掉了Result中其他一些数据
         return  result.getText();
     }
 
     /**
      * 将BufferedImage对象输出到指定的文件中。
     
      * @param image
      * @param destFile
      * @throws IOException
      */
     public  static  final  void  writeToFile(BufferedImage image, File destFile) 
             throws  IOException {
         ImageIO.write(image, FORMAT, destFile);
     }
     
     /**
      * 将一个BitMatrix对象转换成BufferedImage对象
     
      * @param matrix
      * @return
      */
     private  static  BufferedImage toBufferedImage(BitMatrix matrix) {
         int  width = matrix.getWidth();
         int  height = matrix.getHeight();
         BufferedImage image =  new  BufferedImage(
                 width, height, BufferedImage.TYPE_INT_RGB);
         for  ( int  x =  0 ; x < width; x++) {
             for  ( int  y =  0 ; y < height; y++) {
                 image.setRGB(x, y, matrix.get(x, y) ? BLACK : WHITE);
             }
         }
         return  image;
     }
     
     /**
      * 把传入的原始图像按高度和宽度进行缩放,生成符合要求的图标。
     
      * @param srcImageFile 源文件地址
      * @param height 目标高度
      * @param width 目标宽度
      * @param hasFiller 比例不对时是否需要补白:true为补白; false为不补白;
      * @throws IOException
      */
     private  static  BufferedImage scaleImage(String srcImageFile,  int  height, 
             int  width,  boolean  hasFiller)  throws  IOException {
         double  ratio =  0.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();
             }
             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;
     }
}

方法的作用和用法在文档中都写得很清楚啦,就不需要解释了。下面是执行结果:

163943_QJLB_1434710.png

下面是生成的二维码:

103213_nhnX_1434710.jpg    103213_GqNS_1434710.jpg

下面是生成的一维码:

103249_okLZ_1434710.jpg

大家可以用手机扫描试试看。

目录
相关文章
|
8月前
|
小程序
微信小程序 - 二维码数据解析,如何扫码进入开发版测试二维码数据
微信小程序 - 二维码数据解析,如何扫码进入开发版测试二维码数据
300 0
|
2月前
|
JSON fastjson 数据格式
几种Json工具包的解析速度对比
几种Json工具包的解析速度对比
|
2月前
|
Java API Spring
开源!一款基于Spring Boot的二维码生成和解析工具
开源!一款基于Spring Boot的二维码生成和解析工具
39 1
|
3月前
|
JavaScript
js生成二维码和解析二维码
js生成二维码和解析二维码
25 0
|
4月前
|
Java API Apache
Java之打印流,压缩流,工具包的详细解析
4. 打印流 4.1 概述 平时我们在控制台打印输出,是调用print方法和println方法完成的,这两个方法都来自于java.io.PrintStream类,该类能够方便地打印各种数据类型的值,是一种便捷的输出方式。 4.2 PrintStream类
32 0
|
8月前
|
传感器 数据采集 编解码
|
8月前
|
传感器 API Android开发
|
8月前
|
Android开发
ZXing源码解析二:掌握解码步骤2
ZXing源码解析二:掌握解码步骤
|
8月前
|
uml Android开发
ZXing源码解析二:掌握解码步骤1
ZXing源码解析二:掌握解码步骤
|
8月前
|
Java 开发工具 Android开发
ZXing源码解析一:让源码跑起来
ZXing源码解析一:让源码跑起来

推荐镜像

更多