公司让我开发几个对接硬件设备(门禁人脸识别机)等,有多台不同型号的人脸识别机,每种型号需要参考不同的厂家协议,再开发过程中遇到了很多坑,故需要将这些坑提炼出来,避免以后继续踩坑,如下:
- 硬件设备需要配置访问服务器的心跳URL、上传考勤数据URL和拍照图片上传URL,设备是主动通过这三个接口请求,并带相关参数(协议上提供)访问以上三个URL,服务器在接收到请求后给以json格式的响应,其中心跳是具有一定时间间隔的,每个时间间隔会访问一次心跳URL,服务器在每次心跳过来时查询数据库,查找是否有需要下发的数据(即新增人员信息或删除人员信息)以协议上规定的格式下发给硬件设备,硬件设备根据下发的规定格式(JSON)的数据更新设备人员信息到设备数据库。当有人员门禁信息比如人员人脸发生识别时会产生门禁记录,则会主动调用设置的上传考勤数据URL访问服务器对应接口,然后将数据以协议上规定格式上传服务器,服务器接收数据后保存数据库对应表。
- 有些硬件设备会提供操作设备API,而这种API的请求分为form-data类型或application/x-www-form-urlencoded类型,其中form-data中的POST方式采用以下代码:
URL url = new URL(strurl);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "application/json;charset=utf-8");
connection.setRequestProperty("SOAPAction", "");
connection.setConnectTimeout(15000);
connection.setReadTimeout(60000);
connection.connect();
out = connection.getOutputStream();
connection.getOutputStream().write(param.getBytes("UTF-8"));
out.flush();
out.close();
if (connection.getResponseCode() == 200) {
is = connection.getInputStream();
br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
StringBuffer sbf = new StringBuffer();
String temp = null;
while ((temp = br.readLine()) != null) {
sbf.append(temp);
sbf.append("\r\n");
}
result = sbf.toString();
}
- GET方式是不能在body里面带有数据的,不能有**
connection.setDoOutput(true) **,故采用以下代码:
HttpURLConnection connection = null;
InputStream is = null;
BufferedReader br = null;
String result = null;
OutputStream out = null;
Date d1 = new Date();
try {
URL url = new URL(strurl);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setDoInput(true);
connection.setConnectTimeout(15000);
connection.setReadTimeout(60000);
connection.connect();
if (connection.getResponseCode() == 200) {
is = connection.getInputStream();
br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
StringBuffer sbf = new StringBuffer();
String temp = null;
while ((temp = br.readLine()) != null) {
sbf.append(temp);
sbf.append("\r\n");
}
result = sbf.toString();
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != br) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != is) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
connection.disconnect();
}
- 设备是人脸图片都是采用Base64位存储和解析人脸数据,包括三种方案:base64位图片下发,将图片上传到云服务器,然后传递url,或者前两种方式综合提供,设备拍照也是传递base64位字符串给后台,后台可以将其存储到数据库字段,代码如下:
ImgBase64.getImgStr(path+"static\\temp\\"+filename)
import org.apache.commons.codec.binary.Base64;
import sun.misc.BASE64Decoder;
import java.io.*;
public class ImgBase64 {
public static String getImgStr(String imgFile) {
InputStream in = null;
byte[] data = null;
try {
in = new FileInputStream(imgFile);
data = new byte[in.available()];
in.read(data);
in.close();
} catch (IOException e) {
e.printStackTrace();
}
return Base64.encodeBase64String(data);
}
public static boolean generateImage(String imgStr, String imgFilePath) {
if (imgStr == null)
return false;
BASE64Decoder decoder = new BASE64Decoder();
try {
byte[] b = decoder.decodeBuffer(imgStr);
for (int i = 0; i < b.length; ++i) {
if (b[i] < 0) {
b[i] += 256;
}
}
OutputStream out = new FileOutputStream(imgFilePath);
out.write(b);
out.flush();
out.close();
return true;
} catch (Exception e) {
return false;
}
}
}
# 数据库如果存储二进制数据,可以将二进制数据读出,并将其转换为临时图片文件存储到云服务器,然后转换为base64位字符串传递给用户
byte[] data=(byte[]) map.get("fileblob");
if(data.length>0) {
InputStream in = new ByteArrayInputStream(data);
File p = new File(path+"static\\temp");
if(!p.exists()){
p.mkdirs();
}
FileUtil.getFile(in,path+"static\\temp\\"+filename);
- 硬件设备调试可能出现各种未知错误,协议可能没写全,比如可能是图片清晰度不够、图片文件超过设备允许范围,各大参数是否必须,这些都需要重视将其设备返回信息打印出来,因为在客户那里是不允许上外网的,也不能现场调试,所以必须有足够多的日志信息输出,比如接口进入日志,接口传递参数,方法名和参数值调用,调用远程接口返回值一定要都打印出来,以便了解远程报错情况,可以通过AOP切面方式捕获服务接口和方法调用情况。