零、
近日在做课设的时候,碰到一个比较有趣的玩意。就是在应用上添加扫描二维码/条形码的技术。
下面,介绍一下本文采用的一些框架:
-
SpringBoot+Gradle+JPA为框架的后端系统
-
JavaScript(JQuery)编写的前端系统,其中涉及的HTTP协议发包,接收返回的JSON格式字符串皆采用JavaScript。
-
二维码扫描方面采用了Google提供的zxing。
最终结果如下图所示,即通过扫描条形码,获得条形码号,最终成功借入书籍。
一、准备工作
首先,先需要通过Gradle导入zxing包,对应的代码如下:
compile group: 'org.json', name: 'json', version: '20180813'
compile group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.12.0'
compile 'com.google.zxing:core:3.3.0'
compile 'com.google.zxing:javase:3.3.0'
其中,第三第四行是zxing包
下面,定义前后端对接的接口如下:
{
"account":String,
"img":String
}
说明:
·account是借书者的帐号(学号)
·img想借的书的条形码照片
后端返回格式:
{
"msg":String
}
这里,前端格式是基于Http MultiFilePart的,而后端格式是基于JSON的。前端在发图的时候,应当将图片编码成Base64格式的字符串。在完成了一些基础的定义后,就可以开始正式的编写工作了。
二、后端代码编写
首先先编写SpringBoot中Controller类的代码,主要负责读取数据:
@RequestMapping(value = "service/order.book")
public String receive_order_request(MultipartHttpServletRequest request) {
String account=request.getParameter("account");
String file=request.getParameter("img");
//System.out.println("Account="+account);
String s= bookEngine.translateBorrowBook(account,file);
System.out.println(s);
return s;
}
再编写Service类代码,进行图片信息的获取。这里Base64ToImage函数可以将字符串重新编码成图片,并写入相对路径中的目录下。
String bar="";
if(Base64ToImage(file,"tmp.jpg"))
bar=decodeBar("tmp.jpg");
System.out.println("FIND BAR="+bar);
核心的decodeBar函数如下,告知图片所在位置即可:
public String decodeBar(String imgPath)throws Exception{
BufferedImage image = null;
Result result = null;
try {
image = ImageIO.read(new File(imgPath));
if (image == null) {
System.out.println("the decode image may be not exit.");
}
LuminanceSource source = new BufferedImageLuminanceSource(image);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
Hashtable<DecodeHintType, Object> hints = new Hashtable<>();
hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
//hints.put(DecodeHintType.PURE_BARCODE, Boolean.TRUE);
result = new MultiFormatReader().decode(bitmap,hints);
return result.getText();
} catch (Exception e) {
throw e;
}
}
三、前端代码编写
前端代码设计到的问题主要有2点,一是如何调用摄像头进行图像获取,二是如何采用JS进行发包。第一个问题主要可以通过canvas和video标签实现。要注意的为id为video和canvas的两个标签。
<div class="slider_bg" id="mainContainer">
<div class="container">
<div id="content">
<div class="word">
<h1>TIPS:请将书本条形码对准窗口</h1>
<div id="form">
<form style="margin-left: -200px;margin-top: 20px;" action="" >
<table height="253" id="table">
<tr>
<td ><video id="video" width="200px" height="150px" autoplay="autoplay" muted="muted" style="margin-left: 70px;margin-top: 20px;"></video></td>
</tr>
<tr>
<td ><a id="resulttxt" style="margin-left: 90px;margin-top: 20px;">尚未检测出书本</a></td>
</tr>
</table>
</form>
</div>
</div>
</div>
<div class="mainBox"style="margin-left: 60px;margin-top: -60px">
<div class="newBook">
<div class="bookTip"></div>
<div class="bookInfo">
<table id="borrowedlist" width="200px" class="bookTip" style="margin-left: 60px;">
<tr ><th>您的借书:</th></tr>
</table>
</div>
</div>
</div>
<canvas id="canvas" width="400px" height="300px" style="visibility: hidden"></canvas>
</div>
</div>
下面,需要获取video上的图片,然后保存到canvas上(已设置隐藏),再通过ajax发包发给服务器端,这里takePhoto函数每4秒钟执行一次。
<script type = "text/javascript">
window.onload = function()
{
getMedia();
window.setInterval("takePhoto()",4000);
}
function getMedia() {
let constraints = {
video: {width: 200, height: 150},
audio: true
};
//获得video摄像头区域
let video = document.getElementById("video");
//这里介绍新的方法,返回一个 Promise对象
// 这个Promise对象返回成功后的回调函数带一个 MediaStream 对象作为其参数
// then()是Promise对象里的方法
// then()方法是异步执行,当then()前的方法执行完后再执行then()内部的程序
// 避免数据没有获取到
let promise = navigator.mediaDevices.getUserMedia(constraints);
promise.then(function (MediaStream) {
video.srcObject = MediaStream;
video.play();
});
}
function takePhoto() {
//获得Canvas对象
let video = document.getElementById("video");
let canvas = document.getElementById("canvas");
var f=video
//alert(f)
let ctx = canvas.getContext('2d');
ctx.drawImage(video, 0, 0, 400, 300);
//alert("GUGU")
var imgData = canvas.toDataURL("image/jpg");
//alert("PAPA")
var base64Data = imgData.split(",")[1];
//alert(base64Data)
//alert("DUDU")
var fd=new FormData();
fd.append('img',base64Data)
fd.append("account",sessionStorage.getItem("userAccount"))
//alert("RUSH")
$.ajax({
type: "POST",
url: "http://localhost:8080/service/order.book",
processData:false, //tell jQuery not to process the data
contentType: false, //tell jQuery not to set contentType
data:fd,
success: function (res) {
//var res=tmp.parseJSON();
//alert("tmp="+tmp.toString()+"JSON="+res.toString())
if(res.toString()==="SUCCESS") {
$("#resulttxt").html("<a>成功借书</a>")
window.location.replace(window.location.href.substring(0, window.location.href.lastIndexOf('/')) + "/borrow.html")
}
else if(res.toString()==="ERROR_FULL"){
$("#resulttxt").html("<a >借书失败,借书已满</a>");
}
else if(res.toString()==="ERROR_NOBOOK") {
$("#resulttxt").html("<a >借书失败,此书已被借走</a>");
}
else if(res.toString()==="BORROWED"){
$("#resulttxt").html("<a >您已借入此书</a>")
}else{
$("#resulttxt").html("<a >图像不清</a>")
}
},
error: function (XMLHttpRequest, textStatus) {
var txt4="<a >"+"请将书本条形码对准窗口:"+"</a>";
$("#resulttxt").html(txt4);
alert(textStatus + ":" + XMLHttpRequest.status + " " + XMLHttpRequest.readyState);
}
})
}
</script>
完整的Github开源代码如下,代码中涉及到了基于规则的系统。