1. WEB项目实现二维码识别
1.1. 问题
一般实现二维码扫描都是app调用手机,或者调用微信的库即可。但是由于web项目直接调用app的库,而调用微信的扫一扫功能必须在微信上使用。因此要在web项目中实现二维码识别的话,就要自己重新搞一下了。不弄不知道,发现二维码识别涉及的东西还是蛮多的。
1.2. 实现思路
在WEB项目上面实现二维码识别思路,前端用h5实现调用手机摄像头拍照,然后将照片上传到后端。在后端将二维码图片识别,再将相关信息返回处理。
1.3. 前端
1.3.1. html
1 <!DOCTYPE html>
2
3 <html>
4
5 <head>
6
7 <meta charset="utf-8">
8
9 <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0,
10
11 maximum-scale=1.0, minimum-scale=1.0">
12
13 <title>二维码识别</title>
14
15 <style>
16
17 *{ margin: 0; padding: 0;}
18
19 </style>
20
21 </head>
22
23 <body>
24
25 <button id="Sys" type="button" class="layui-btn" style="margin-left: 22px;" >
26
27 <img style="width:26px;" alt="扫一扫" src="/images/erweima.png"> 扫一扫
28
29 </button>
30
31 </div>
32
33 </div>
34
35 <!-- </form> -->
36
37 </div>
38
39
40
41 <input type="file" id='p_image' accept="image/*" capture='camera' style="opacity: 0"/>
42
43 <img id="tempImage" src="" style="display:none;"/>
44
45
46
47 <script src="/jquery/jquery-1.11.3.js"></script> <!-- 你必须先引入jQuery1.8或以上版本 -->
48
49 <script type="text/javascript" src="/js/exif.js"></script>
50
51 <script type="text/javascript" src="/js/sys.js"></script>
52
53 </body>
54
55 </html>
1.3.2. sys.js
上传图片需要对图片进行压缩,转化成base64字符串
1 // 扫一扫
2
3 $("#Sys").on('click', function(){
4
5 $("#p_image").click();
6
7 });
8
9
10
11 //处理扫一扫
12
13 $(function () {
14
15 $("#p_image").change(function (e) {
16
17 var file = e.currentTarget.files[0];
18
19
20
21 //创建一个文件读取的工具类
22
23 var reader = new FileReader();
24
25 //这里利用了闭包的特性,来保留文件名
26
27 (function (x) {
28
29 reader.onload = function (e) {
30
31 //调用压缩文件的方法,具体实现逻辑见下面
32
33 render(this.result, x);
34
35 }
36
37 })(file.name);
38
39 //告诉文件读取工具类读取那个文件
40
41 reader.readAsDataURL(file);
42
43 });
44
45
46
47 });
48
49
50
51 //压缩图片
52
53 function render(src) {
54
55 // 需要压缩的最大尺寸
56
57 var MAX_SIZE = 500;
58
59 //创建Image对象
60
61 var image = new Image();
62
63 //图片方向角
64
65 var orientation = null;
66
67 image.src = src;
68
69 image.onload = function () {
70
71 var canvas = document.createElement("canvas");
72
73 //获取2d画布
74
75 var ctx = canvas.getContext("2d");
76
77 canvas.width = image.width;
78
79 canvas.height = image.height;
80
81 ctx.clearRect(0, 0, canvas.width, canvas.height);
82
83 //绘制图片
84
85 ctx.drawImage(image, 0, 0, image.width, image.height);
86
87 //获取照片方向角属性,用户旋转控制
88
89 EXIF.getData(image, function() {
90
91 EXIF.getAllTags(this);
92
93 orientation = EXIF.getTag(this,'Orientation');
94
95 });
96
97
98
99 //通过固定的宽高比压缩
100
101 //宽大于高的情况
102
103 if (image.width > MAX_SIZE && image.width >= image.height) {
104
105 image.height *= MAX_SIZE / image.width;
106
107 image.width = MAX_SIZE;
108
109 } else if (image.height > MAX_SIZE && image.height > image.width) {//宽小于高的情况
110
111 image.width *= MAX_SIZE / image.height;
112
113 image.height = MAX_SIZE;
114
115 }
116
117 canvas.width = image.width;
118
119 canvas.height = image.height;
120
121 ctx.clearRect(0, 0, canvas.width, canvas.height);
122
123 //绘制图片
124
125 ctx.drawImage(image, 0, 0, image.width, image.height);
126
127 //生成base64码
128
129 var base64Code = canvas.toDataURL("image/png");
130
131 $("#tempImage").attr("src", base64Code);
132
133 };
134
135
136
137
138
139 var img = document.getElementById('tempImage');
140
141 img.onload = function(){
142
143 var canvas = document.createElement("canvas");
144
145 //获取2d画布
146
147 var ctx = canvas.getContext("2d");
148
149 canvas.width = img.width;
150
151 canvas.height = img.height;
152
153 ctx.clearRect(0, 0, canvas.width, canvas.height);
154
155 //绘制图片
156
157 ctx.drawImage(img, 0, 0, img.width, img.height);
158
159 //如果方向角不为1,都需要进行旋转 added by lzk
160
161 if(orientation != "" && orientation != 1){
162
163 switch(orientation){
164
165 case 6://需要顺时针(向左)90度旋转
166
167 rotateImg(img,'left',canvas);
168
169 break;
170
171 case 8://需要逆时针(向右)90度旋转
172
173 rotateImg(img,'right',canvas);
174
175 break;
176
177 case 3://需要180度旋转
178
179 rotateImg(img,'right',canvas);//转两次
180
181 rotateImg(img,'right',canvas);
182
183 break;
184
185 }
186
187 }
188
189 var base64Code = canvas.toDataURL("image/png");
190
191 //$("#myImage1").attr("src", base64Code);
192
193 //调用上传图片方法
194
195 send(base64Code);
196
197 }
198
199
200
201 }
202
203 //上传图片
204
205 function send(baseData){
206
207 var index = layer.load(1, {time: 15*1000}); //加载提示弹窗,并且设定最长等待15秒
208
209 $.ajax({
210
211 url: '/scanFile.action',
212
213 type: 'post',
214
215 data: {"baseData":baseData},
216
217 dataType: 'json',
218
219 success: function (data) {
220
221 if(data.resultcode == "success"){
222
223 $("#FIRMNAME").val(data["ENTERPRISENAME"]);
224
225 $("#USCCODE").val(data["USCCODE"]);
226
227 layer.close(index);
228
229 } else {
230
231 layer.msg(data["resultcontent"]);
232
233 layer.close(index);
234
235 }
236
237 }
238
239 });
240
241 }
242
243 //旋转图片
244
245 function rotateImg(img, direction,canvas) {
246
247 //最小与最大旋转方向,图片旋转4次后回到原方向
248
249 var min_step = 0;
250
251 var max_step = 3;
252
253 if (img == null)return;
254
255 //img的高度和宽度不能在img元素隐藏后获取,否则会出错
256
257 var height = img.height;
258
259 var width = img.width;
260
261 var step = 2;
262
263 if (step == null) {
264
265 step = min_step;
266
267 }
268
269 if (direction == 'right') {
270
271 step++;
272
273 //旋转到原位置,即超过最大值
274
275 step > max_step && (step = min_step);
276
277 } else {
278
279 step--;
280
281 step < min_step && (step = max_step);
282
283 }
284
285 //旋转角度以弧度值为参数
286
287 var degree = step * 90 * Math.PI / 180;
288
289 var ctx = canvas.getContext('2d');
290
291 switch (step) {
292
293 case 0:
294
295 canvas.width = width;
296
297 canvas.height = height;
298
299 ctx.drawImage(img, 0, 0);
300
301 break;
302
303 case 1:
304
305 canvas.width = height;
306
307 canvas.height = width;
308
309 ctx.rotate(degree);
310
311 ctx.drawImage(img, 0, -height);
312
313 break;
314
315 case 2:
316
317 canvas.width = width;
318
319 canvas.height = height;
320
321 ctx.rotate(degree);
322
323 ctx.drawImage(img, -width, -height);
324
325 break;
326
327 case 3:
328
329 canvas.width = height;
330
331 canvas.height = width;
332
333 ctx.rotate(degree);
334
335 ctx.drawImage(img, -width, 0);
336
337 break;
338
339 }
340
341 }
1.4. 后端解析二维码
1.4.1. java解析二维码
使用Zxing.jar直接对二维码图片进行解析。
1 import java.awt.image.BufferedImage;
2
3 import java.io.ByteArrayOutputStream;
4
5 import java.io.File;
6
7 import java.io.IOException;
8
9 import java.util.HashMap;
10
11 import java.util.Map;
12
13
14
15 import javax.imageio.ImageIO;
16
17
18
19 import com.google.zxing.BarcodeFormat;
20
21 import com.google.zxing.BinaryBitmap;
22
23 import com.google.zxing.ChecksumException;
24
25 import com.google.zxing.DecodeHintType;
26
27 import com.google.zxing.EncodeHintType;
28
29 import com.google.zxing.FormatException;
30
31 import com.google.zxing.MultiFormatWriter;
32
33 import com.google.zxing.NotFoundException;
34
35 import com.google.zxing.Result;
36
37 import com.google.zxing.WriterException;
38
39 import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
40
41 import com.google.zxing.client.j2se.MatrixToImageWriter;
42
43 import com.google.zxing.common.BitMatrix;
44
45 import com.google.zxing.common.HybridBinarizer;
46
47 import com.google.zxing.qrcode.QRCodeReader;
48
49 import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
50
51
52
53 /**
54
55 * 二维码工具类
56
57 * @author limingcheng
58
59 *
60
61 */
62
63 public class QrCodeUtil {
64
65
66
67 /**
68
69 * 生成一个二维码图片
70
71 * @param width
72
73 * @param height
74
75 * @param content
76
77 * @return
78
79 * @throws WriterException
80
81 * @throws IOException
82
83 */
84
85 public static byte[] createQRCode(int width, int height, String content) throws WriterException, IOException {
86
87 // 二维码基本参数设置
88
89 Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>();
90
91 hints.put(EncodeHintType.CHARACTER_SET, "utf-8");// 设置编码字符集utf-8
92
93 hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);// 设置纠错等级L/M/Q/H,纠错等级越高越不易识别,当前设置等级为最高等级H
94
95 hints.put(EncodeHintType.MARGIN, 0);// 可设置范围为0-10,但仅四个变化0 1(2) 3(4 5 6) 7(8 9 10)
96
97 // 生成图片类型为QRCode
98
99 BarcodeFormat format = BarcodeFormat.QR_CODE;
100
101 // 创建位矩阵对象
102
103 BitMatrix bitMatrix = new MultiFormatWriter().encode(content, format, width, height, hints);
104
105 // 设置位矩阵转图片的参数
106
107 // MatrixToImageConfig config = new MatrixToImageConfig(Color.black.getRGB(), Color.white.getRGB());
108
109 // 位矩阵对象转流对象
110
111 ByteArrayOutputStream os = new ByteArrayOutputStream();
112
113 MatrixToImageWriter.writeToStream(bitMatrix, "png", os);
114
115 return os.toByteArray();
116
117 }
118
119
120
121 /**
122
123 * 解析二维码
124
125 * @throws FormatException
126
127 * @throws ChecksumException
128
129 * @throws NotFoundException
130
131 * @throws IOException
132
133 */
134
135 public static void parsingQrCode() throws NotFoundException, ChecksumException, FormatException, IOException {
136
137 QRCodeReader formatReader = new QRCodeReader();
138
139 BufferedImage image;
140
141 File file = new File("E:\\bestme.png");
142
143 // 识别图片中的二维码内容
144
145 image = ImageIO.read(file);
146
147 // image = ImageUtil.binarization(image);
148
149 BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(new BufferedImageLuminanceSource(image)));
150
151
152
153 //定义二维码参数
154
155 HashMap hints = new HashMap();
156
157 // 解码设置编码方式为:utf-8
158
159 hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
160
161 // 优化精度
162
163 hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
164
165 //复杂模式,开启PURE_BARCODE模式
166
167 // hints.put(DecodeHintType.PURE_BARCODE, Boolean.TRUE);
168
169 Result result = formatReader.decode(binaryBitmap,hints);
170
171
172
173 System.out.println("解析结果"+result.toString());
174
175 System.out.println("二维码类型"+result.getBarcodeFormat());
176
177 System.out.println("二维码内容"+result.getText());
178
179 }
180
181
182
183 public static void main(String[] args) throws WriterException, IOException, NotFoundException, ChecksumException, FormatException {
184
185 parsingQrCode();
186
187
188
189 // byte[] b = createQRCode(100, 100, "遇见最好的自己!");
190
191 // OutputStream os = new FileOutputStream("E:\\bestme.png");
192
193 // os.write(b);
194
195 // os.close();
196
197 }
198
199 }
输出结果:
解析结果遇见最好的自己!
二维码类型QR_CODE
二维码内容遇见最好的自己!
注意:使用复杂模式会导致图片识别不了,具体原因暂没清晰。