一、html5
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" src="http://51.onelink.ynwlzc.net/o2o/tpl/Merchant/static/js/jquery.min.js"></script>
<style type="text/css">
html {
height: 100%;
width: 100%;
}
#readCard {
width: 90px;
margin: 10px;
}
#output {
width: 500px;
height: 200px;
}
</style>
</head>
<body>
<h1>读身份证示例-湖南步联科技身份证
</h1> <h1>未来之窗星链-推进器测试
</h1><br>
<input type="button" id="getReader" value="列出设备" style="margin:10px" onclick="getReader();" />
<select id="readerList" style="width:250px;margin:10px"></select>
<!-- 读卡参数<input type="text" id="readParam" value='{"portType": 5}' size="25" style="margin:10px" > -->
<br/>
<input type="button" id="readCard" value="读卡" onclick="startReadCard();" />
<input type="checkbox" id="autoReadCard" value="autoRead" >自动读卡
<br>
阅读次数:<input type="text" id="totalReadCount" name="Num" value="100" size="7">
成功次数<input type="text" id="SuccessTimes" value="0" size="7">
失败次数<input type="text" id="FailTimes" value="0" size="7">
所用时间<input type="text" id="TimeExpand" value="0" size="7">
成功率<input type="text" id="Rate" value="0%" size="5">
平均耗时<input type="text" id="AvgTime" value="0" size="5">
<br>
开始时间<input type="text" id="StartTime" value="" size="25">
完成时间<input type="text" id="EndTime" value="" size="25">
单次用时<input type="text" id="ReadTime" value="111" size="25">
<table border="1" cellpadding="0" cellspacing="2" bordercolor="#3333FF">
<tr>
<td><div align="right">姓名:</div></td>
<td><input name="text_name" type="text" id="text_name" size="20"/></td>
<td><div align="right">性别代码:</div></td>
<td><input name="text_genderid" type="text" id="text_genderid" size="5"/></td>
<td><div align="right">民族代码:</div></td>
<td><input name="text_nationid" type="text" id="text_nationid" size="5"/></td>
<td><div align="right">出生日期:</div></td>
<td><input name="text_birthdate" type="text" id="text_birthdate" size="20"/></td>
</tr>
<tr>
<td><div align="right">身份证号码:</div></td>
<td><input name="text_idnumber" type="text" id="text_idnumber" size="20"/></td>
<td><div align="right">签发机关:</div></td>
<td><input name="text_signorgan" type="text" id="text_signorgan" size="20"/></td>
<td><div align="right">起始有效期:</div></td>
<td><input name="text_beginterm" type="text" id="text_beginterm" size="20"/></td>
<td><div align="right">终止有效期:</div></td>
<td><input name="text_endterm" type="text" id="text_endterm" size="20"/></td>
</tr>
<tr>
<td><div align="right">住址:</div></td>
<td colspan="5"><input name="text_address" type="text" id="text_address" size="82"/>
</td>
<td><div align="right">民族:</div></td>
<td><input name="text_nationid" type="text" id="text_nation" size="5"/></td>
</tr>
<tr>
<td height="43">
<div align="right">照片编码:</div>
</td>
<td colspan="7"><textarea name="text_photobase64" cols="80" rows="5" id="text_photobase64"></textarea></td>
<td><img name="PhotoDisplay" id="PhotoDisplay" style="width:102px; height:126px;"/></td>
</tr>
</table>
<br/>
<textarea id="output"></textarea><br/>
</body>
</html>
<script type="text/javascript" >
var baseUrl = "http://127.0.0.1:6045"
$(document).ready(function(){
//解决IE浏览器不支持console,报错未定义问题
window.console = window.console || (function () {
var c ={};
c.log = c.warn = c.debug = c.info = c.error = c.time = c.dir = c.profile= c.clear = c.exception = c.trace = c.assert = function(){};
return c;
})();
// alert(location.host)
// if(location.host){
// baseUrl = location.protocol + "//" + location.host;
// }
console.log("baseurl "+ baseUrl);
});
function fixInt(val){
if(val<10)
return "0"+val;
else
return val;
}
function fmtDate(date){
return date.getFullYear()+"-"+fixInt(date.getMonth()+1)+"-"+fixInt(date.getDate())+" "+fixInt(date.getHours())+":"+fixInt(date.getMinutes())+":"+fixInt(date.getSeconds())+":"+fixInt(date.getMilliseconds());
}
function clearForm() {
$("#output").text("");
$("#text_name").val("");
$("#text_genderid").val("");
$("#text_nationid").val("");
$("#text_birthdate").val("");
$("#text_address").val("");
$("#text_idnumber").val("");
$("#text_signorgan").val("");
$("#text_beginterm").val("");
$("#text_endterm").val("");
}
function getReader(){
$.ajax({
type: "get",
dataType: "json",
timeout:10000,
crossDomain: true,
url: baseUrl + "/getCardReaderList",
success: function(resultInfo) {
$("#output").text(JSON.stringify(resultInfo));
var list = document.getElementById("readerList");
for(var i = list.options.length-1; i >= 0; i--){
list.remove(i);
}
var strArray = resultInfo;
for(var i = 0; i < resultInfo.length; i++){
var newOption = document.createElement("option");
newOption.setAttribute("version", strArray[i].version);
newOption.setAttribute("index", strArray[i].index);
newOption.appendChild(document.createTextNode(strArray[i].version));
list.appendChild(newOption);
}
},
error: function(jqXHR, textStatus, errorThrown) {
console.error("getReader request error ", textStatus, errorThrown );
$("#output").text("getReader error status "+ textStatus+" errorThrown " +errorThrown)
}
});
}
function parseCardWzxx(wzxx) {
var result = {};
result['xm'] = wzxx.substr(0, 15);
result['xbdm'] = wzxx.substr(15, 1);
result['mzdm'] = wzxx.substr(16, 2);
result['csrq'] = wzxx.substr(18, 8);
result['dzmc'] = wzxx.substr(26, 35);
result['gmsfhm'] = wzxx.substr(61, 18);
result['qfjgmc'] = wzxx.substr(79, 15);
result['qfjgmc'] = wzxx.substr(79, 15);
result['yxqqsrq'] = wzxx.substr(94, 8);
result['yxqjzrq'] = wzxx.substr(102, 8);
result['zjlxbs'] = wzxx.substr(124, 1);
switch (result['xbdm']) {
case '1': result['xbmc'] = '男'; break;
case '2': result['xbmc'] = '女'; break;
default: result['xbmc'] = '未知';
}
if (result['zjlxbs'] == 'J'){
result['txzhm'] = wzxx.substr(110, 9);
result['qfcs'] = wzxx.substr(119, 2);
}
switch (result['mzdm']) {
case '01': result['mzmc'] = '汉'; break;
case '02': result['mzmc'] = '蒙古'; break;
case '03': result['mzmc'] = '回'; break;
case '04': result['mzmc'] = '藏'; break;
case '05': result['mzmc'] = '维吾尔'; break;
case '06': result['mzmc'] = '苗'; break;
case '07': result['mzmc'] = '彝'; break;
case '08': result['mzmc'] = '壮'; break;
case '09': result['mzmc'] = '布依'; break;
case '10': result['mzmc'] = '朝鲜'; break;
case '11': result['mzmc'] = '满'; break;
case '12': result['mzmc'] = '侗'; break;
case '13': result['mzmc'] = '瑶'; break;
case '14': result['mzmc'] = '白'; break;
case '15': result['mzmc'] = '土家'; break;
case '16': result['mzmc'] = '哈尼'; break;
case '17': result['mzmc'] = '哈萨克'; break;
case '18': result['mzmc'] = '傣'; break;
case '19': result['mzmc'] = '黎'; break;
case '20': result['mzmc'] = '傈僳'; break;
case '21': result['mzmc'] = '佤'; break;
case '22': result['mzmc'] = '畲'; break;
case '23': result['mzmc'] = '高山'; break;
case '24': result['mzmc'] = '拉祜'; break;
case '25': result['mzmc'] = '水'; break;
case '26': result['mzmc'] = '东乡'; break;
case '27': result['mzmc'] = '纳西'; break;
case '28': result['mzmc'] = '景颇'; break;
case '29': result['mzmc'] = '柯尔克孜'; break;
case '30': result['mzmc'] = '土'; break;
case '31': result['mzmc'] = '达翰尔'; break;
case '32': result['mzmc'] = '仫佬'; break;
case '33': result['mzmc'] = '羌'; break;
case '34': result['mzmc'] = '布朗'; break;
case '35': result['mzmc'] = '撒拉'; break;
case '36': result['mzmc'] = '毛南'; break;
case '37': result['mzmc'] = '仡佬'; break;
case '38': result['mzmc'] = '锡伯'; break;
case '39': result['mzmc'] = '阿昌'; break;
case '40': result['mzmc'] = '普米'; break;
case '41': result['mzmc'] = '塔吉克'; break;
case '42': result['mzmc'] = '怒'; break;
case '43': result['mzmc'] = '乌孜别克'; break;
case '44': result['mzmc'] = '俄罗斯'; break;
case '45': result['mzmc'] = '鄂温克'; break;
case '46': result['mzmc'] = '德昂'; break;
case '47': result['mzmc'] = '保安'; break;
case '48': result['mzmc'] = '裕固'; break;
case '49': result['mzmc'] = '京'; break;
case '50': result['mzmc'] = '塔塔尔'; break;
case '51': result['mzmc'] = '独龙'; break;
case '52': result['mzmc'] = '鄂伦春'; break;
case '53': result['mzmc'] = '赫哲'; break;
case '54': result['mzmc'] = '门巴'; break;
case '55': result['mzmc'] = '珞巴'; break;
case '56': result['mzmc'] = '基诺'; break;
case '59': result['mzmc'] = '穿青人'; break;
case '60': result['mzmc'] = '革家人'; break;
case '97': result['mzmc'] = '其它'; break;
case '98': result['mzmc'] = '入籍'; break;
case '99': result['mzmc'] = '其它'; break;
default: result['mzmc'] = '';
}
return result;
}
var totalStartTime;
var totalReadCount;
var SuccessTimes = 0;
var FailTimes = 0;
var totalUsedTime = 0;
function startReadCard(){
totalStartTime = new Date();
totalReadCount = $("#totalReadCount").val();
SuccessTimes = 0;
FailTimes = 0;
totalUsedTime = 0;
readCard();
}
function readCard(){
clearForm();
var startTime = new Date();
$("#StartTime").val(fmtDate(startTime));
$("#readCard").attr("disabled", "disabled");
var readerIndex = $("#readerList").find("option:selected").attr("index");
console.info("read card", readerIndex)
$.ajax({
type: "get",
dataType: "json",
timeout:10000,
url: baseUrl + "/readIDCard?index="+readerIndex,
success: function(resultInfo) {
console.info("readIDCard ", resultInfo, status);
$("#output").text(JSON.stringify(resultInfo));
if( resultInfo.result === 0 ){
var textInfo = resultInfo.wzInfo;
$("#text_name").val(textInfo.substr(0,15).trim());
$("#text_genderid").val(textInfo.substr(15,1).trim());
$("#text_nationid").val(textInfo.substr(16,2).trim());
$("#text_birthdate").val(textInfo.substr(18,8).trim());
$("#text_address").val(textInfo.substr(26,35).trim());
$("#text_idnumber").val(textInfo.substr(61,18).trim());
$("#text_signorgan").val(textInfo.substr(79,15).trim());
$("#text_beginterm").val(textInfo.substr(94,8).trim());
$("#text_endterm").val(textInfo.substr(102,8).trim());
var wzObj = parseCardWzxx(textInfo);
$("#text_nation").val(wzObj.mzmc);
console.info("read card success", wzObj) ;
$.ajax({
type: "get",
dataType: "json",
timeout:10000,
url:baseUrl +"/wltUnpack?wlt="+resultInfo.zpWlt+"&format=bmp",
success:function(response){
if(response['result'] === 0){
var imageBase64 = response['image'];
$("#text_photobase64").val(imageBase64);
document.all['PhotoDisplay'].src = 'data:image/bmp;base64,' + imageBase64;
}else{
alert("unpack error " + response['result']);
}
},
complete: function(XMLHttpRequest, textStatus) {
}
});
}
var endTime = new Date();
$("#EndTime").val(fmtDate(endTime));
var usedTime = endTime.getTime()-startTime.getTime();
$("#ReadTime").val(usedTime + "ms");
if ( $('#autoReadCard').is(":checked") && totalReadCount--> 0){
self.setTimeout("readCard()", 1000);
totalUsedTime += usedTime;
$("#TimeExpand").val(totalUsedTime + "ms");
if(resultInfo.result == 0){
++SuccessTimes;
}else{
++FailTimes;
}
$("#SuccessTimes").val(SuccessTimes);
$("#FailTimes").val(FailTimes);
$("#Rate").val(SuccessTimes*100/(SuccessTimes+FailTimes) + "%");
$("#AvgTime").val(totalUsedTime/(SuccessTimes+FailTimes) + "ms");
}else{
$("#readCard").removeAttr("disabled");
}
},
complete: function(XMLHttpRequest, textStatus) {
// alert("cloudReadCard/appId complete");
console.info("complete");
},
error: function(jqXHR, textStatus, errorThrown) {
console.error("read card request error ", textStatus, errorThrown );
$("#output").text("read card error status "+ textStatus+" errorThrown " +errorThrown)
$("#readCard").removeAttr("disabled");
}
});
}
</script>
二、JAVA
import com.bland.IDReader;
import org.json.JSONArray;
import org.json.JSONObject;
import java.util.Base64;
import java.io.*;
public class demo{
static public String race_table[][] ={
{"01","汉族"},// GB/T 3304-1991","Han","HA
{"02","蒙古族"},// GB/T 3304-1991","Mongol","MG
{"03","回族"},// GB/T 3304-1991","Hui","HU
{"04","藏族"},// GB/T 3304-1991","Zang","ZA
{"05","维吾尔族"},// GB/T 3304-1991","Uygur","uG
{"06","苗族"},// GB/T 3304-1991","Miao","MH
{"07","彝族"},// GB/T 3304-1991","Yi","YI
{"08","壮族"},// GB/T 3304-1991","Zhuang","ZH
{"09","布依族"},// GB/T 3304-1991","Buyei","BY
{"10","朝鲜族"},// GB/T 3304-1991","Chosen","cs
{"11","满族"},// GB/T 3304-1991","Man","MA
{"12","侗族"},// GB/T 3304-1991","Dong","DO
{"13","瑶族"},// GB/T 3304-1991","Yao","YA
{"14","白族"},// GB/T 3304-1991","Bai","BA
{"15","土家族"},// GB/T 3304-1991","Tujia","TJ
{"16","哈尼族"},// GB/T 3304-1991","Hani","HN
{"17","哈萨克族"},// GB/T 3304-1991","Kazak","KZ
{"18","傣族"},// GB/T 3304-1991","Dai","DA
{"19","黎族"},// GB/T 3304-1991","Li","LI
{"20","傈僳族"},// GB/T 3304-1991","Lisu","LS
{"21","佤族"},// GB/T 3304-1991","Va","VA
{"22","畲族"},// GB/T 3304-1991","She","SH
{"23","高山族"},// GB/T 3304-1991","Gaoshan","Gs
{"24","拉枯族"},// GB/T 3304-1991","Lahu","LH
{"25","水族"},// GB/T 3304-1991","Sui","su
{"26","东乡族"},// GB/T 3304-1991","Dongxiang","DX
{"27","纳西族"},// GB/T 3304-1991","Naxi","NX
{"28","景颇族"},// GB/T 3304-1991","Jingpo","JP
{"29","柯尔克孜族"},// GB/T 3304-1991","Kirgiz","KG
{"30","土族"},// GB/T 3304-1991","Tu","TU
{"31","达斡尔族"},// GB/T 3304-1991","Daur","DU
{"32","仫佬族"},// GB/T 3304-1991","Mulao","ML
{"33","羌族"},// GB/T 3304-1991","Qiang","QI
{"34","布朗族"},// GB/T 3304-1991","Blang","BL
{"35","撒拉族"},// GB/T 3304-1991","Salar","SL
{"36","毛南族"},// GB/T 3304-1991","Maonan","MN
{"37","仡佬族"},// GB/T 3304-1991","Gelao","GL
{"38","锡伯族"},// GB/T 3304-1991","Xibe","XB
{"39","阿昌族"},// GB/T 3304-1991","Achang","AC
{"40","普米族"},// GB/T 3304-1991","Pumi","PM
{"41","塔吉克族"},// GB/T 3304-1991","Tajik","TA
{"42","怒族"},// GB/T 3304-1991","Nu","NU
{"43","乌孜别克族"},// GB/T 3304-1991","Uzbek","Uz
{"44","俄罗斯族"},// GB/T 3304-1991","Russ","RS
{"45","鄂温克族"},// GB/T 3304-1991","Ewenki","EW
{"46","德昂族"},// GB/T 3304-1991","Deang","DE
{"47","保安族"},// GB/T 3304-1991","Bonan","BN
{"48","裕固族"},// GB/T 3304-1991","Yugur","YG
{"49","京族"},// GB/T 3304-1991","Gin","GI
{"50","塔塔尔族"},// GB/T 3304-1991","Tatar","TT
{"51","独龙族"},// GB/T 3304-1991","Derung","DR
{"52","鄂伦春族"},// GB/T 3304-1991","Oroqen","OR
{"53","赫哲族"},// GB/T 3304-1991","Hezhen","HZ
{"54","门巴族"},// GB/T 3304-1991","Monba","MB
{"55","珞巴族"},// GB/T 3304-1991","Lhoba","LB
{"56","基诺族"},// GB/T 3304-1991","Jino","JN
{"",""}
};
/**
* 将民族代码,转换成文本
* @param code
* @return
*/
public static String GetRace(String code) {
int i;
for (i = 0; i < race_table.length; i++) {
if (race_table[i][0].compareTo(code) == 0) {
return race_table[i][1];
}
}
return "其它";
}
/**
* 显示身份证信息
* @param text 文本
* @param image 照片
*/
public static void ViewInfo(String text,byte[] image) {
System.out.println(text);
try
{
File imgFile = new File("image.jpg");
FileOutputStream writer = new FileOutputStream(imgFile);
writer.write(image);
writer.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
/**
* 解析SN
* @param str JSON 格式字符串
* @return 返回错误或成功信息
*/
public static int ParseSN(String str) {
byte[] img;
String info = "";
try {
JSONObject jsondata = new JSONObject(str);
if(jsondata.has("errorMsg")) {
//System.out.println(jsondata.getString("errorMsg"));
return -1;
}
if( !jsondata.has("data") ) {
return -1;
}
JSONObject content = jsondata.getJSONObject("data");
System.out.println("SAM ID: " + content.getString("samid"));
if(content.has("sn")) {
System.out.println("SN: " + content.getString("sn"));
}
}
catch (Exception e) {
e.printStackTrace();
return -1;
}
return 0;
}
/**
* 解析身份证读卡结果
* @param str JSON 格式字符串
* @return 返回错误或成功信息
*/
public static String ParseIDInfo(String str) {
byte[] img;
String info = "";
try {
JSONObject jsondata = new JSONObject(str);
if(jsondata.getString("function").compareTo("readcard") != 0) {
return "";//不是读卡消息,直接返回
}
if(jsondata.has("errorMsg")) {
System.out.println(jsondata.getString("errorMsg"));
return jsondata.getString("errorMsg");
}
if( !jsondata.has("data") ) {
return "";
}
JSONObject content = jsondata.getJSONObject("data");
if(content.getString("type").compareTo("I") == 0) {
info = "外国人永久居留证";
}
else if(content.getString("type").compareTo("J") == 0) {
info = "港澳台居住证";
}
else if(content.getString("type").compareTo(" ") == 0) {
info = "居民身份证";
}
else {
info = "未知";
}
info = info + "\r\n姓名:" + content.getString("name");
info = info + "\r\n身份证号:" + content.getString("number");
String gender;
if(content.getInt("gender") == 1) {
gender = "男";
}
else if (content.getInt("gender") == 2){
gender = "女";
}
else {
gender = "未知";
}
info = info + "\r\n性别: " + gender;
if(content.getString("type").compareTo(" ") == 0) {
//只有身份证有民族信息
info = info + "\r\n民族: " + GetRace(content.getString("race"));
}
info = info + "\r\n生日: " + content.getString("birthday");
if(content.getString("type").compareTo("I") != 0 ) {
//外国人永久居留证没有住址信息
info = info + "\r\n住址: " + content.getString("address");
}
info = info + "\r\n签发机关: " + content.getString("issuer");
info = info + "\r\n有效期限: " + content.getString("valied") + "-" + content.getString("expire");
if(content.getString("type").compareTo("I") == 0) {
//外国人永久居留证
info = info + "\r\n中文名称: " + content.getString("cn_name");
info = info + "\r\n版本: " + content.getString("version");
info = info + "\r\n国籍: " + content.getString("nation");
}
else if(content.getString("type").compareTo("J") == 0) {
//港澳台居住证
info = info + "\r\n通行证号码: " + content.getString("pass_number");
info = info + "\r\n签发次数: " + content.getString("issue_count");
}
info = info + "\r\n";
img = Base64.getDecoder().decode(content.getString("photo"));
ViewInfo(info,img);
return "读卡成功!";
}
catch (Exception e) {
e.printStackTrace();
return e.getMessage();
}
}
/**
* 解析银行卡读卡结果
* @param str JSON 格式字符串
* @return 返回错误或成功信息
*/
public static String ParseBankInfo(String str) {
byte[] img;
String info = "";
try {
JSONObject jsondata = new JSONObject(str);
if(jsondata.has("errorMsg")) {
System.out.println(jsondata.getString("errorMsg"));
return jsondata.getString("errorMsg");
}
if( !jsondata.has("data") ) {
return "";
}
JSONObject content = jsondata.getJSONObject("data");
info = info + "\r\n银行卡号:" + content.getString("number");
info = info + "\r\n有效期截止: " + content.getString("expire");
info = info + "\r\n持卡人:" + content.getString("owner");
info = info + "\r\n持卡人证件号码: " + content.getString("owner_id");
info = info + "\r\n";
System.out.println(info);
return "读卡成功!";
}
catch (Exception e) {
e.printStackTrace();
return e.getMessage();
}
}
/**
* 解析读卡结果的返回值
* @param str JSON 格式字符串
* @return 返回读卡结果的返回值
*/
public static int GetRetValue(String str) {
try {
JSONObject jsondata = new JSONObject(str);
if(jsondata.has("ret")) {
return jsondata.getInt("ret");
}
else {
return -1;
}
}
catch (Exception e) {
e.printStackTrace();
return -1;
}
}
public static void main(String [] args) {
String systemType = System.getProperty("os.name");
String systemArch = System.getProperty("os.arch");
IDReader reader = new IDReader();
System.out.println("System Type: " + systemType + ", " + systemArch);
//提供一个可读写的目录,用于初始化过程中读写配置,解压调用库文件。初始化只能调用一次。
reader.Init("./res");
System.out.println("demo start!");
try
{
while(true) {
Thread.sleep(100);
String result = reader.WebSocketAPI("{\"module\":\"idcard\",\"msgid\":\"0\",\"function\":\"getsam\"}");
if(ParseSN(result) == 0) {
break;
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
try
{
while(true) {
String result;
result = reader.WebSocketAPI("{\"module\":\"idcard\",\"msgid\":\"0\",\"function\":\"readcard\"}");
if( GetRetValue(result) == 0) {
ParseIDInfo(result);
Thread.sleep(1000);
continue;
}
result = reader.WebSocketAPI("{\"module\":\"iccard\",\"msgid\":\"0\",\"function\":\"iso14443a_find\"}");
if( GetRetValue(result) == 0) {
//读银行卡号码
result = reader.WebSocketAPI("{\"module\":\"iccard\",\"msgid\":\"0\",\"function\":\"pboc_read\"}");
ParseBankInfo(result);
Thread.sleep(1000);
continue;
}
Thread.sleep(100);
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
三、PYTHON
# -*- coding: utf-8 -*-
# import urllib2
import urllib.request
import json
import time
race = ["","汉族","蒙古族","回族","藏族","维吾尔族","苗族","彝族","壮族","布依族","朝鲜族","满族","侗族","瑶族","白族","土家族","哈尼族","哈萨克族","傣族","黎族","傈僳族","佤族","畲族","高山族","拉枯族","水族","东乡族","纳西族","景颇族","柯尔克孜族","土族","达斡尔族","仫佬族","羌族","布朗族","撒拉族","毛南族","仡佬族","锡伯族","阿昌族","普米族","塔吉克族","怒族","乌孜别克族","俄罗斯族","鄂温克族","德昂族","保安族","裕固族","京族","塔塔尔族","独龙族","鄂伦春族","赫哲族","门巴族","珞巴族" ,"基诺族"]
gender = ["未知","男","女","其他"]
while 1:
time.sleep(1)
url = "http://127.0.0.1:7846/api/readCard?utf8=1"
response = urllib.request.urlopen(url)
page_html = response.read()
#print(page_html)
result = json.loads(page_html)
if result["resultFlag"] == "0":
print("读卡成功:\n")
print("身份证号码:"+result["resultContent"]["idNum"])
print("姓名:"+result["resultContent"]["name"])
print("性别:"+gender[int(result["resultContent"]["gender"])])
print("民族:"+race[int(result["resultContent"]["nation"])])
print("地址:"+result["resultContent"]["address"])
print("生日:"+result["resultContent"]["birthday"])
print("有效期: "+result["resultContent"]["effectDate"] + "-" + result["resultContent"]["expireDate"] )
print("签发机关: "+result["resultContent"]["issueOrg"])
continue
url = "http://127.0.0.1:7846/readBankCard"
response = urllib.request.urlopen(url)
page_html = response.read()
#print(page_html)
result = json.loads(page_html)
if result["result"] == 0 :
print("读卡成功:\n")
print("银行卡号码:"+result["number"])
print("有效期截止:"+result["expire"])
print("持卡人:"+result["owner"])
print("持卡人证件号码:"+result["owner_id"])
四、VB
Public Class Form1
Dim port As reader_serviceLib.sdt = New reader_serviceLib.sdt()
Dim card As reader_serviceLib.card
Private Sub refreshButton()
Dim ret As Integer
ret = port.opened()
If ret = 0 Then
btOpen.Enabled = True
btReadCard.Enabled = False
btReadBank.Enabled = False
btOpen.Text = "打开"
Else
btOpen.Enabled = True
btReadCard.Enabled = True
btReadBank.Enabled = True
btOpen.Text = "关闭"
If ret > 1000 Then
TextStatus.Text = "已打开USB端口" & ret
Else
TextStatus.Text = "已打开串口COM" & ret
End If
TextSAMID.Text = port.samid()
End If
End Sub
Private Sub btReadCard_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btReadCard.Click
Dim ret As Integer
ret = port.find()
If ret <> &H9F Then
TextStatus.Text = "找卡失败!"
Exit Sub
End If
ret = port.select()
If ret <> &H90 Then
TextStatus.Text = "选卡失败!"
Exit Sub
End If
TextCardSN.Text = port.cardSN(0)
card = port.readcard(0)
If card.name = "" Then
TextStatus.Text = "读卡失败!"
Exit Sub
End If
TextName.Text = card.name
TextAddress.Text = card.address
TextBirthday.Text = card.birthday
TextGender.Text = card.gender
TextIssuer.Text = card.issuer
TextRace.Text = card.race
TextNumber.Text = card.number
TextExpired.Text = card.valied & "-" & card.expire
PictureBox1.ImageLocation = card.image_path
End Sub
Private Sub btOpen_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btOpen.Click
Dim ret As Integer
If ComboPortList.Text = "USB" Then
ret = port.open(1001, 115200)
ElseIf ComboPortList.Text.StartsWith("COM") Then
ret = port.open(Integer.Parse(ComboPortList.Text.Substring(3)), 115200)
Else
ret = port.open(0, 0)
End If
If ret <> &H90 Then
TextStatus.Text = "端口打开失败!"
Exit Sub
End If
Call refreshButton()
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
refreshButton()
End Sub
Private Sub btReadBank_Click(sender As Object, e As EventArgs) Handles btReadBank.Click
Dim uid As reader_serviceLib.iso14443a_uid
Dim obj()
Dim ret As Integer
obj = port.iso14443a_find(4)
If obj.Length = 0 Then
TextStatus.Text = "找卡失败!"
' 必须要退出 ISO14443A 模式,否则下次无法读取身份证
Call port.iso14443a_mode_exit()
Exit Sub
End If
uid = obj(0)
ret = port.iso14443a_select(uid.text, uid.sak)
If ret <> &H90 Then
' 必须要退出 ISO14443A 模式,否则下次无法读取身份证
Call port.iso14443a_mode_exit()
Call MsgBox("选卡失败!")
Exit Sub
End If
'参数0代表读身份证,参数1代表读银行卡
card = port.readcard(1)
If card.number = "" Then
' 必须要退出 ISO14443A 模式,否则下次无法读取身份证
Call port.iso14443a_mode_exit()
Call MsgBox("读卡失败!")
Exit Sub
End If
Call MsgBox("银行卡号:" & card.number & ";有效期:" & card.expire & ";持卡人:" & card.name)
' 必须要退出 ISO14443A 模式,否则下次无法读取身份证
Call port.iso14443a_mode_exit()
End Sub
End Class
五、安卓
package com.example.demo;
import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import com.bland.IDReader;
import com.bland.TpNfc;
import com.google.android.material.snackbar.Snackbar;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Message;
import android.os.Handler;
import android.view.View;
import android.app.PendingIntent;
import android.content.Context;
import android.hardware.usb.UsbDevice;
import android.widget.Toast;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;
import android.content.Intent;
import android.content.IntentFilter;
import com.example.demo.databinding.ActivityMainBinding;
import java.util.HashMap;
import android.view.Menu;
import android.view.MenuItem;
import android.nfc.tech.NfcB;
import org.json.JSONArray;
import org.json.JSONObject;
import android.provider.Settings;
import android.content.Context;
import android.nfc.Tag;
import android.nfc.NfcAdapter;
import android.util.Log;
import java.util.HashMap;
import android.util.Base64;
import android.widget.ImageView;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
//用于身份证解码先关变量
public String race_table[][] ={
{"01","汉族"},// GB/T 3304-1991","Han","HA
{"02","蒙古族"},// GB/T 3304-1991","Mongol","MG
{"03","回族"},// GB/T 3304-1991","Hui","HU
{"04","藏族"},// GB/T 3304-1991","Zang","ZA
{"05","维吾尔族"},// GB/T 3304-1991","Uygur","uG
{"06","苗族"},// GB/T 3304-1991","Miao","MH
{"07","彝族"},// GB/T 3304-1991","Yi","YI
{"08","壮族"},// GB/T 3304-1991","Zhuang","ZH
{"09","布依族"},// GB/T 3304-1991","Buyei","BY
{"10","朝鲜族"},// GB/T 3304-1991","Chosen","cs
{"11","满族"},// GB/T 3304-1991","Man","MA
{"12","侗族"},// GB/T 3304-1991","Dong","DO
{"13","瑶族"},// GB/T 3304-1991","Yao","YA
{"14","白族"},// GB/T 3304-1991","Bai","BA
{"15","土家族"},// GB/T 3304-1991","Tujia","TJ
{"16","哈尼族"},// GB/T 3304-1991","Hani","HN
{"17","哈萨克族"},// GB/T 3304-1991","Kazak","KZ
{"18","傣族"},// GB/T 3304-1991","Dai","DA
{"19","黎族"},// GB/T 3304-1991","Li","LI
{"20","傈僳族"},// GB/T 3304-1991","Lisu","LS
{"21","佤族"},// GB/T 3304-1991","Va","VA
{"22","畲族"},// GB/T 3304-1991","She","SH
{"23","高山族"},// GB/T 3304-1991","Gaoshan","Gs
{"24","拉枯族"},// GB/T 3304-1991","Lahu","LH
{"25","水族"},// GB/T 3304-1991","Sui","su
{"26","东乡族"},// GB/T 3304-1991","Dongxiang","DX
{"27","纳西族"},// GB/T 3304-1991","Naxi","NX
{"28","景颇族"},// GB/T 3304-1991","Jingpo","JP
{"29","柯尔克孜族"},// GB/T 3304-1991","Kirgiz","KG
{"30","土族"},// GB/T 3304-1991","Tu","TU
{"31","达斡尔族"},// GB/T 3304-1991","Daur","DU
{"32","仫佬族"},// GB/T 3304-1991","Mulao","ML
{"33","羌族"},// GB/T 3304-1991","Qiang","QI
{"34","布朗族"},// GB/T 3304-1991","Blang","BL
{"35","撒拉族"},// GB/T 3304-1991","Salar","SL
{"36","毛南族"},// GB/T 3304-1991","Maonan","MN
{"37","仡佬族"},// GB/T 3304-1991","Gelao","GL
{"38","锡伯族"},// GB/T 3304-1991","Xibe","XB
{"39","阿昌族"},// GB/T 3304-1991","Achang","AC
{"40","普米族"},// GB/T 3304-1991","Pumi","PM
{"41","塔吉克族"},// GB/T 3304-1991","Tajik","TA
{"42","怒族"},// GB/T 3304-1991","Nu","NU
{"43","乌孜别克族"},// GB/T 3304-1991","Uzbek","Uz
{"44","俄罗斯族"},// GB/T 3304-1991","Russ","RS
{"45","鄂温克族"},// GB/T 3304-1991","Ewenki","EW
{"46","德昂族"},// GB/T 3304-1991","Deang","DE
{"47","保安族"},// GB/T 3304-1991","Bonan","BN
{"48","裕固族"},// GB/T 3304-1991","Yugur","YG
{"49","京族"},// GB/T 3304-1991","Gin","GI
{"50","塔塔尔族"},// GB/T 3304-1991","Tatar","TT
{"51","独龙族"},// GB/T 3304-1991","Derung","DR
{"52","鄂伦春族"},// GB/T 3304-1991","Oroqen","OR
{"53","赫哲族"},// GB/T 3304-1991","Hezhen","HZ
{"54","门巴族"},// GB/T 3304-1991","Monba","MB
{"55","珞巴族"},// GB/T 3304-1991","Lhoba","LB
{"56","基诺族"},// GB/T 3304-1991","Jino","JN
{"",""}
};
private static final int SDK_NFC_FLAG = 3;
private static final int SDK_READER_FLAG = 4;
private static final int SDK_DEVICE_FLAG = 5;
private static final int SDK_CLIENT_FLAG = 6;
private static final int SDK_ICCARD_FLAG = 7;
private static final int SDK_BANKCARD_FLAG = 8;
//用于 OTG 阅读器相关变量
public IDReader reader;
public UsbManager usbManager;
private static final String ACTION_USB_PERMISSION = "com.android.usb.USB_PERMISSION";
private boolean id_card_exist = false; // 标记上一次读卡时,身份证是否存在
private String samid = ""; // 标记阅读器是否连接,以及SAM ID是多少
private String last_idcard_text = ""; // 最近一次身份证读卡信息,方便判断身份证是否变化
private String last_iccard_text = ""; // 最近一次Mifare one读卡数据信息
private String last_iccard_uid = ""; // 最近一次Mifare one 读卡UID信息
private int last_iccard_sak = 0; // 最近一次Mifare one 读卡SAK信息
//用于手机本机 NFC 相关变量
public TpNfc tpnfc;
NfcAdapter mNfcAdapter;
PendingIntent pIntent;
//其他变量
private AppBarConfiguration appBarConfiguration;
private ActivityMainBinding binding;
/**
* 将民族代码,转换成文本
* @param code
* @return
*/
public String GetRace(String code) {
int i;
for (i = 0; i < race_table.length; i++) {
if (race_table[i][0].compareTo(code) == 0) {
return race_table[i][1];
}
}
return "其它";
}
/**
* 在 UI 上显示身份证信息
* @param text 文本
* @param image 照片
*/
void ViewInfo(String text,Bitmap image) {
TextView tv = (TextView) findViewById(R.id.textview_first);
ImageView iv = (ImageView) findViewById(R.id.imageView);
tv.setText(text.toCharArray(), 0, text.toCharArray().length);
iv.setImageBitmap(image);
}
/**
* 解析身份证读卡结果
* @param str JSON 格式字符串
* @return 返回错误或成功信息
*/
String ParseInfo(String str) {
Bitmap decodedByte = null;
String info = "";
try {
JSONObject jsondata = new JSONObject(str);
if(jsondata.getString("function").compareTo("readcard") != 0) {
return "";//不是读卡消息,直接返回
}
if(jsondata.has("errorMsg")) {
ViewInfo(info,decodedByte); //卡片已拿走或其他错误,清空信息
return jsondata.getString("errorMsg");
}
if( !jsondata.has("data") ) {
//异常情况,不应该出现,清空信息
ViewInfo(info,decodedByte);
return "";
}
JSONObject content = jsondata.getJSONObject("data");
if(content.getString("type").compareTo("I") == 0) {
info = "2017版外国人永久居留证";
}
else if(content.getString("type").compareTo("Y") == 0) {
info = "新版外国人永久居留证";
}
else if(content.getString("type").compareTo("J") == 0) {
info = "港澳台居住证";
}
else if(content.getString("type").compareTo(" ") == 0) {
info = "居民身份证";
}
else {
info = "未知";
}
info = info + "\r\n姓名:" + content.getString("name");
info = info + "\r\n身份证号:" + content.getString("number");
String gender;
if(content.getInt("gender") == 1) {
gender = "男";
}
else if (content.getInt("gender") == 2){
gender = "女";
}
else {
gender = "未知";
}
info = info + "\r\n性别: " + gender;
if(content.getString("type").compareTo(" ") == 0) {
//只有身份证有民族信息
info = info + "\r\n民族: " + GetRace(content.getString("race"));
}
info = info + "\r\n生日: " + content.getString("birthday");
if(content.getString("type").compareTo("I") != 0 ) {
//外国人永久居留证没有住址信息
info = info + "\r\n住址: " + content.getString("address");
}
info = info + "\r\n签发机关: " + content.getString("issuer");
info = info + "\r\n有效期限: " + content.getString("valied") + "-" + content.getString("expire");
if(content.getString("type").compareTo("I") == 0) {
//2017版外国人永久居留证
info = info + "\r\n中文名称: " + content.getString("cn_name");
info = info + "\r\n版本: " + content.getString("version");
info = info + "\r\n国籍: " + content.getString("nation");
}
else if(content.getString("type").compareTo("Y") == 0) {
//新版外国人永久居留证
info = info + "\r\n中文名称: " + content.getString("cn_name");
info = info + "\r\n换证次数: " + content.getString("renewal_count");
info = info + "\r\n关联号码: " + content.getString("relational_number");
info = info + "\r\n国籍: " + content.getString("nation");
}
else if(content.getString("type").compareTo("J") == 0) {
//港澳台居住证
info = info + "\r\n通行证号码: " + content.getString("pass_number");
info = info + "\r\n签发次数: " + content.getString("issue_count");
}
byte[] img = Base64.decode(content.getString("photo"),Base64.DEFAULT);
decodedByte = BitmapFactory.decodeByteArray(img, 0, img.length);
ViewInfo(info,decodedByte);
return "读卡成功!";
}
catch (Exception e) {
return e.getMessage();
}
}
/**
* 处理身份证阅读消息,或者 OTG 阅读器插入拔出消息
*/
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
String err = "";
switch (msg.what) {
case SDK_NFC_FLAG: {
err = ParseInfo((String)msg.obj);
break;
}
case SDK_READER_FLAG: {
err = ParseInfo((String)msg.obj);
break;
}
case SDK_DEVICE_FLAG:{
if(samid == "" ) {
err = "阅读器已拔出!";
}
else {
err = "阅读器已连接:" + samid;
}
break;
}
case SDK_CLIENT_FLAG: {
ViewInfo((String)msg.obj,null);
break;
}
case SDK_ICCARD_FLAG: {
if(last_iccard_uid != "") {
ViewInfo("uid: " + last_iccard_uid + "\r\ndata: " + last_iccard_text, null);
}
else {
ViewInfo("",null);
}
break;
}
case SDK_BANKCARD_FLAG: {
if(last_iccard_uid != "") {
ViewInfo("uid: " + last_iccard_uid + "\r\n" + (String)msg.obj, null);
}
else {
ViewInfo("",null);
}
break;
}
}
if (err != "") {
Snackbar.make(findViewById(R.id.fab), err, Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
}
};
/**
* 申请 USB OTG 阅读器访问权限
* @param usbManager
* @return
*/
public int UsbPermission(UsbManager usbManager) {
//单独申请USB权限
PendingIntent permissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE);
HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();
for (UsbDevice usbDevice : deviceList.values()) {
if( reader.CompareReaderID(usbDevice.getVendorId(),usbDevice.getProductId()) ) {
if(!usbManager.hasPermission(usbDevice)) {
usbManager.requestPermission(usbDevice, permissionIntent);
}
else {
}
Log.e("id_reader","Reader found!");
break;
}
else {
}
Log.e("id_reader","Name: " + usbDevice.getManufacturerName() + " " + usbDevice.getProductName() + ", VID: " + usbDevice.getVendorId() + ",PID: " + usbDevice.getProductId());
}
return 0;
}
/**
* 发送消息到 UI
* @param str
*/
public void SendMsg(String str,int type) {
Message msg = new Message();
msg = new Message();
if(mHandler != null) {
msg.what = type;
msg.obj = str;
mHandler.sendMessage(msg);
}
}
/**
* 发送 OTG 阅读器连接或拔出的消息到 UI
* @param samid
*/
public void SendDeviceMsg(String samid) {
SendMsg(samid,SDK_DEVICE_FLAG);
}
/**
* 发送身份证读卡结果消息到 UI
* @param result 身份证读卡结果,JSON格式
* @param type
*/
public void SendCardInfoMsg(String result,int type) {
//tpnfc.SetLastRead(result);
SendMsg(result,type);
}
/**
* 判断 OTG 阅读器身份证读卡结果是否有变化,以决定是否需要发送消息到 UI
* @param result
* @return 返回身份证是否存在于OTG 阅读器
*/
public boolean OnIDCardResult(String result) {
try {
boolean cur_exist;
JSONObject jsondata = new JSONObject(result);
if(jsondata.getInt("ret") != 0 ) {
if (jsondata.getInt("ret") == 2) {
//阅读器设备状态改变,变为不存在,发送通知消息
samid = "";
SendDeviceMsg(samid);
}
last_idcard_text = "";
cur_exist = false;
}
else {
cur_exist = true;
}
if(jsondata.has("data")) {
JSONObject data = jsondata.getJSONObject("data");
if(data.has("text")) {
String cur_text = data.getString("text");
if (cur_text.compareTo(last_idcard_text) != 0) {//卡片信息发生改变,发送通知消息
id_card_exist = true;
last_idcard_text = cur_text;
SendCardInfoMsg(result, SDK_READER_FLAG);
return id_card_exist;
}
}
}
if(cur_exist != id_card_exist) {//卡片状态发生改变,发送通知消息
id_card_exist = cur_exist;
SendCardInfoMsg(result, SDK_READER_FLAG);
return id_card_exist;
}
}
catch (Exception e) {
id_card_exist = false;
}
return id_card_exist;
}
/**
* 判断 OTG Mifare one 读卡结果是否有变化,以决定是否需要发送消息到 UI
* @param result
* @return 返回Mifare one 卡是否存在
*/
public boolean OnICCardResult(String result) {
try {
JSONObject jsondata = new JSONObject(result);
if(jsondata.getInt("ret") != 0 ) {
if (jsondata.getInt("ret") == 2) {
//阅读器设备状态改变,变为不存在,发送通知消息
samid = "";
SendDeviceMsg(samid);
}
last_iccard_text = "";
last_iccard_sak = 0;
}
String cmd = jsondata.getString("function");
if(jsondata.has("data")) {
JSONObject data = jsondata.getJSONObject("data");
if( cmd.compareTo("iso14443a_find") == 0) {//处理找卡结果
JSONArray uid = data.getJSONArray("uid");
if(uid.length()>0) {
last_iccard_uid = uid.getJSONObject(0).getString("text");
last_iccard_sak = uid.getJSONObject(0).getInt("sak");
return true;
}
else {
}
}
else if(cmd.compareTo("pboc_read") == 0 && data.has("number") ) {
if(last_iccard_text.compareTo(data.getString("number")) !=0 ) {
last_iccard_text = data.getString("number");
String bankInfo = "银行卡号:"+data.getString("number") + "\r\n";
bankInfo += "有效期截止:" + data.getString("expire") + "\r\n";
bankInfo += "持卡人:" + data.getString("owner") + "\r\n";
bankInfo += "持卡人证件号码:" + data.getString("owner_id") + "\r\n";
SendCardInfoMsg(bankInfo, SDK_BANKCARD_FLAG);
}
return true;
}
else if( data.has("hex") ){//处理读到的数据
if(last_iccard_text.compareTo(data.getString("hex")) !=0 ) {
reader.WebSocketAPI("{\"module\":\"common\",\"msgid\":\"0\",\"function\":\"beep\",\"data\":{\"length\":200}}");
last_iccard_text = data.getString("hex");
SendCardInfoMsg(result, SDK_ICCARD_FLAG);
}
return true;
}
}
if(last_iccard_text != "" || last_iccard_uid != "") {//卡片状态发生改变,发送通知消息
last_iccard_uid = "";
last_iccard_text = "";
last_iccard_sak = 0;
SendCardInfoMsg(result, SDK_ICCARD_FLAG);
}
return false;
}
catch (Exception e) {
return false;
}
}
/**
* 清除读卡结果,并发送消息到 UI
*/
public void ClearReaderResult() {
if( samid.compareTo("") != 0 ){
samid = "";
SendDeviceMsg(samid);
}
if(id_card_exist) {
id_card_exist = false;
SendCardInfoMsg("{\"module\":\"idcard\",\"msgid\":\"0\",\"function\":\"readcard\",\"errorMsg\":\"设备不存在\",\"ret\":2}", SDK_READER_FLAG);
}
last_idcard_text = "";
}
/**
* 读取 OTG 阅读器的 SAM ID,并返回 OTG 阅读器是否存在
* @return 返回 OTG 阅读器是否存在
*/
public boolean CheckSamID() {
String result;
JSONObject jsondata;
try {
if(samid == "" ) {
result = reader.WebSocketAPI("{\"module\":\"idcard\",\"msgid\":\"0\",\"function\":\"getsam\"}");
jsondata = new JSONObject(result);
if(jsondata.has("errorMsg") ) {
if(jsondata.has("ret") && jsondata.getInt("ret") == 2) {
//设备不存在
}
return false;
}
if(!jsondata.has("data") ) {
return false;
}
JSONObject data = jsondata.getJSONObject("data");
if(!data.has("samid") ) {
return false;
}
samid = data.getString("samid");
if(samid == "" ) {
return false;
}
SendDeviceMsg(samid);
}
return true;
}
catch(Exception e) {
return false;
}
}
/**
* 使用 OTG 阅读器读取身份证,处理读取结果并返回身份证是否存在
* @return 返回身份证是否存在
*/
public boolean ReadIDCard() {
try {
if(!id_card_exist) {
//还没有检测到卡片存在的情况下,可以先找卡和选卡。 这段代码不是必须,可以去掉,一样正常使用
if( ! OnIDCardResult( reader.WebSocketAPI("{\"module\":\"idcard\",\"msgid\":\"0\",\"function\":\"findcard\"}") ) ){
return false;
}
if( !OnIDCardResult( reader.WebSocketAPI("{\"module\":\"idcard\",\"msgid\":\"0\",\"function\":\"selectcard\"}") ) ) {
return false;
}
}
return OnIDCardResult( reader.WebSocketAPI("{\"module\":\"idcard\",\"msgid\":\"0\",\"function\":\"readcard\"}") );
}
catch(Exception e) {
return false;
}
}
/**
* 使用 OTG 阅读器读取Mifare one卡
* @return 返回Mifare one卡是否存在
*/
public boolean ReadICCard() {
String cmd,pre_uid;
if(id_card_exist) {//确认身份证卡片已存在的情况下,不读IC卡,避免冲突
return false;
}
try {
pre_uid = last_iccard_uid;
if (!OnICCardResult(reader.WebSocketAPI("{\"module\":\"iccard\",\"msgid\":\"0\",\"function\":\"iso14443a_find\"}"))) {
return false;
}
if( last_iccard_uid.compareTo(pre_uid) == 0) {
//卡没有发生变化,不重复读了
return true;
}
if((last_iccard_sak & 0x20) == 0x20 ) {
//构造读取银行卡号码的指令
cmd = "{\"module\":\"iccard\",\"msgid\":\"0\",\"function\":\"pboc_read\",\"data\": {\"uid\":\""+ last_iccard_uid + "\"} }";
}
else {
//构造读取 IC 卡的第 0 个Block 的指令
int type = 0x60; //密钥A:0x60 , 密钥B:0x61
String key = "FFFFFFFFFFFF";
int block = 0;
cmd = "{\"module\":\"iccard\",\"msgid\":\"0\",\"function\":\"mifare_read\",\"data\": {\"uid\":\""+ last_iccard_uid + "\",\"block\":"+block+",\"type\":"+ type + ",\"key\":\""+key+"\"} }";
}
return OnICCardResult( reader.WebSocketAPI(cmd) );
}
catch(Exception e) {
return false;
}
}
/**
* OTG 阅读器读取身份证信息线程
*/
Runnable runnable = new Runnable() {
@Override
public void run() {
while(true) {
int msgid = 0;
try {
if( reader.Connected(usbManager) ) {
// 使用串口转网口模块时, reader.Connected(usbManager) 改为 true,直接 if(true) {
if(!CheckSamID()) {
Thread.sleep(1000);
continue;
}
if( ReadIDCard() || ReadICCard()) {
Thread.sleep(100);
continue;
}
Thread.sleep(100);
}
else {
ClearReaderResult();
Thread.sleep(200);
continue;
}
}
catch (Exception e) {
String error = e.toString();
Log.e("id_reader",error);
}
}
}
};
/**
* 感应到身份证时触发该事件。实现手机本机 NFC 读身份证。
* @param intent
*/
@Override
protected void onNewIntent(Intent intent){
super.onNewIntent(intent);
setIntent(intent);
Tag tag = getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG);
if(tag == null ) {
return ;
}
//sendCardInfoMsg("{\"module\" : \"tpnfc\",\"function\" : \"card\", \"data\": {\"id\":\"" + byte2hex(tag.getId()) + "\"},\"ret\":0}",SDK_NFC_FLAG);
NfcB card = NfcB.get(tag);
if(card == null ) {
return;
}
try {
card.connect();
String result = tpnfc.LocalNfcRead(card);
card.close();
JSONObject content = new JSONObject(result);
if(content.has("errorMsg")) {
String error = content.getString("errorMsg");
String s = new String(error.getBytes("ISO-8859-1"), "UTF-8");
String info = "\nerror: " + s + "\n";
}
SendCardInfoMsg(result,SDK_NFC_FLAG);
}
catch (java.io.IOException e){
String result = "{\"module\" : \"idcard\",\"function\" : \"readcard\",\"errorMsg\":\"" + e.getMessage() + "\",\"errorNo\":34, \"server_error_code\": 0 ,\"server_error_string\": \"\"}";
SendCardInfoMsg(result,SDK_NFC_FLAG);
}
catch (Exception e){
String result = "{\"module\" : \"idcard\",\"function\" : \"readcard\",\"errorMsg\":\"" + e.getMessage() + "\",\"errorNo\":34, \"server_error_code\": 0 ,\"server_error_string\": \"\"}";
SendCardInfoMsg(result,SDK_NFC_FLAG);
}
}
/**
* 初始化手机本机 NFC 读身份证的相关变量和事件
* @param path 提供可读写的路径,用于保存会话信息
*/
public void InitNfc(String path){
TpNfc.Init(path);
tpnfc = new TpNfc();
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (null == mNfcAdapter) {
Toast.makeText(this, "不支持NFC功能", Toast.LENGTH_SHORT).show();
TpNfc.nfc_support = 0;
} else if (!mNfcAdapter.isEnabled()) {
TpNfc.nfc_support = 0;
Intent intent = new Intent(Settings.ACTION_NFC_SETTINGS);
// 根据包名打开对应的设置界面
startActivity(intent);
}
else {
TpNfc.nfc_support = 1;
}
pIntent = PendingIntent.getActivity(this, 0,
//在Manifest里或者这里设置当前activity启动模式,否则每次向阳NFC事件,activity会重复创建
// 当然也要按照具体情况来,你设置成singleTask也不是不行,
new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),
PendingIntent.FLAG_MUTABLE);
}
/**
* 初始化 OTG 阅读器相关的变量、事件、权限等等
* @param path 提供可读写的路径,用于保存配置
*/
public void InitReader(String path){
IDReader.ServiceStart(path);
reader = new IDReader();
usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
UsbPermission(usbManager);
new Thread(runnable).start();
}
/**
* 修改手机本机 NFC 的相关设置
*/
@Override
protected void onResume() {
super.onResume();
if (mNfcAdapter != null) {
//添加intent-filter
IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
IntentFilter tag = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
IntentFilter tech = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);
IntentFilter[] filters = new IntentFilter[]{ndef, tag, tech};
String[][] techList = new String[][]{
new String[]{
"android.nfc.tech.Ndef",
"android.nfc.tech.NfcA",
"android.nfc.tech.NfcB",
"android.nfc.tech.NfcF",
"android.nfc.tech.NfcV",
"android.nfc.tech.NdefFormatable",
"android.nfc.tech.MifareClassic",
"android.nfc.tech.MifareUltralight",
"android.nfc.tech.NfcBarcode"
}
};
mNfcAdapter.enableForegroundDispatch(this, pIntent, filters, techList);
}
}
@Override
protected void onPause() {
super.onPause();
if (mNfcAdapter != null) {
mNfcAdapter.disableForegroundDispatch(this);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
String path = getFilesDir().toString();
InitNfc(path);
InitReader(path);
setSupportActionBar(binding.toolbar);
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build();
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
binding.fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(tpnfc == null ) {
return ;
}
/*
使用手机本机 NFC 阅读身份证时,需要先将手机与账号绑定。步骤如下:
1. 官网注册账号
2. 使用 tpnfc.SendBindRequest 向账号发送绑定请求
3. 官网登录账号,接受绑定请求
3. 调用 tpnfc.ClientInfo() 获取信息,判断是否绑定成功。
4. 绑定成功功后,即可正常使用手机NFC读取身份证信息
*/
String info = "";
String user = "18118742537"; //用户账号
try {
JSONObject jsondata = new JSONObject(tpnfc.ClientInfo());
if (jsondata.has("secret") && jsondata.has("limit") && jsondata.has("expire")) {
//已经绑定账号
info = "NFC客户端\n有效期:" + jsondata.getString("expire") + "\n次数限制:" + jsondata.getInt("decode") + "/" + jsondata.getInt("limit");
if(jsondata.getString("secret").compareTo("ok") != 0) {
//客户端认证失败,需要解除绑定后,再重新绑定
info += "\n密钥:密钥丢失,需要重新绑定";
info += "\n解除绑定:" + ParseServerError(tpnfc.Unbind(jsondata.getString("id"), user)); //解除绑定
}
else {
}
} else {
//还未绑定账号, 发送绑定请求, tpnfc.SendBindRequest 第一个参数为 客户端名称, 第二个参数是用户账号(手机号码)
info += "发送绑定请求:";
info += ParseServerError(tpnfc.SendBindRequest("测试NFC客户端", user));
}
}
catch ( Exception e) {
info = e.getMessage();
}
SendMsg(info,SDK_CLIENT_FLAG);
}
});
}
public String ParseServerError(String result) {
String err = "";
try {
JSONObject ret = new JSONObject(result);
if (ret.has("errorMsg")) {
if (ret.getInt("errorNo") == 43) {
err = "操作失败:" + ret.getString("server_error_string");
} else {
err = "操作失败:" + ret.getString("errorMsg");
}
} else {
err = "操作成功!";
}
}
catch (Exception e) {
err = e.getMessage();
}
return err;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
//测试重启服务
reader.ServiceStop();
reader.ServiceStart(getFilesDir().toString());
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean onSupportNavigateUp() {
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
return NavigationUI.navigateUp(navController, appBarConfiguration)
|| super.onSupportNavigateUp();
}
}
六、C#
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace webapi_http
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btReadCard_Click(object sender, EventArgs e)
{
string result;
string url = "http://127.0.0.1:7846/api/readCard?utf8=1";
try
{
HttpWebRequest wbRequest = (HttpWebRequest)WebRequest.Create(url);
wbRequest.Proxy = null;
wbRequest.Method = "GET";
HttpWebResponse wbResponse = (HttpWebResponse)wbRequest.GetResponse();
using (Stream responseStream = wbResponse.GetResponseStream())
{
using (StreamReader sReader = new StreamReader(responseStream))
{
result = sReader.ReadToEnd();
}
}
}
catch (Exception ex)
{
result = ex.Message; //输出捕获到的异常,用OUT关键字输出
}
textResult.Text = result;
}
}
}
七、C
#include <stdio.h>
#include <sdtapi.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#ifndef WIN32
#include <sys/time.h>
#include <sys/stat.h>
#include <dirent.h>
void Sleep(unsigned int x)
{
struct timespec xsleep; \
xsleep.tv_sec = x / 1000; \
xsleep.tv_nsec = (x - xsleep.tv_sec * 1000) * 1000 * 1000; \
nanosleep(&xsleep, NULL); \
}
#else
#include <windows.h>
#include <winnls.h>
#endif
#include "bland.h"
#ifndef WIN32
void print_com_list()//for linux
{
struct dirent *pDirent;
DIR *pDir;
int i,index;
struct stat st;
char path[512] = {0};
pDir = opendir("/sys/class/tty");
if (pDir == NULL) {
return ;
}
for (i=0,index=1;;i++) {
pDirent = readdir(pDir);
if(pDirent == NULL) {
break;
}
snprintf(path,sizeof(path)-1,"/sys/class/tty/%s/device",pDirent->d_name);
if (lstat(path, &st) < 0) {
continue;
}
printf("COM%d: %s\n",index,path);
index++;
}
closedir(pDir);
}
//使用 UTF-8 字符编码
#define GetName GetNameUtf8
#define GetGender GetGenderUtf8
#define GetBirthday GetBirthdayUtf8
#define GetAddress GetAddressUtf8
#define GetRace GetRaceUtf8
#define GetIDNum GetIDNumUtf8
#define GetIssuer GetIssuerUtf8
#define GetValidFrom GetValidFromUtf8
#define GetExpireDate GetExpireDateUtf8
#define GetPassNum GetPassNumUtf8
#define GetIssueCount GetIssueCountUtf8
#define GetNationCode GetNationCodeUtf8
#define GetCnName GetCnNameUtf8
#endif
void print_info2(char * text)
{
char str[256] = {0};
char str2[256] = {0};
int ret,type;
type = GetType(text);
ret = GetName(text,str,sizeof(str));
if(ret < 0) {
goto info_error;
}
printf("name: %s\n",str);
ret = GetGender(text,str,sizeof(str));
if(ret < 0) {
goto info_error;
}
printf("gender: %s\n",str);
if(type == ' ') {
//只有身份证有此项
ret = GetRace(text,str,sizeof(str));
if(ret < 0) {
goto info_error;
}
printf("race: %s\n",str);
}
ret = GetBirthday(text,str,sizeof(str));
if(ret < 0) {
goto info_error;
}
printf("birthday: %s\n",str);
if(type != 'I' && type != 'Y') {
//外国人永久居留证没有此项
ret = GetAddress(text,str,sizeof(str));
if(ret < 0) {
goto info_error;
}
printf("address: %s\n",str);
}
ret = GetIDNum(text,str,sizeof(str));
if(ret < 0) {
goto info_error;
}
printf("IDNum: %s\n",str);
ret = GetIssuer(text,str,sizeof(str));
if(ret < 0) {
goto info_error;
}
printf("issuer: %s\n",str);
ret = GetValidFrom(text,str,sizeof(str));
if(ret < 0) {
goto info_error;
}
ret = GetExpireDate(text,str2,sizeof(str2));
if(ret < 0) {
goto info_error;
}
printf("valid: %s-%s\n",str,str2);
if(type == 'J') {
ret = GetPassNum(text,str,sizeof(str));
if(ret < 0) {
goto info_error;
}
printf("passNum: %s\n",str);
ret = GetIssueCount(text,str,sizeof(str));
if(ret < 0) {
goto info_error;
}
printf("IssueCount: %s\n",str);
}
else if(type == 'I' || type == 'Y') {
ret = GetNationCode(text,str,sizeof(str));
if(ret < 0) {
goto info_error;
}
printf("nation: %s\n",str);
ret = GetCnName(text,str,sizeof(str));
if(ret < 0) {
goto info_error;
}
printf("cnName: %s\n",str);
if(type == 'I') {
//2017版外国人永久居留证
ret = GetCardVersion(text,str,sizeof(str));
if(ret < 0) {
goto info_error;
}
printf("version: %s\n",str);
}
else {
ret = GetRelationalNum(text,str,sizeof(str));
if(ret < 0) {
goto info_error;
}
printf("relational number: %s\n",str);
ret = GetRenewalCount(text,str,sizeof(str));
if(ret < 0) {
goto info_error;
}
printf("renewal count: %s\n",str);
}
}
printf("\n");
info_error:
return ;
}
unsigned int get_sector_num(unsigned int block_num)
{
int sector;
if (block_num < 128)
{
sector = block_num / 4;
}
else
{
sector = ((block_num - 128) / 16) + 32;
}
return sector;
}
#define MAX_UID_FIND 6
//读取银行卡卡号等信息
int ReadBankCard(int iPort, ISO14443A_UID_T * card)
{
int ret;
char number[256] = {0};
char info[sizeof(number)] = {0};
ret = ReadPbocCardNumA(iPort,card->UID,card->UIDLen,number,info,sizeof(number));
if(ret>0) {
printf("Bank Card Number:\t%s\n",number);
printf("Other Information:\t%s\n",info);
return ret;
}
else {
return -1;
}
}
//读取 Mifare IC卡
int ReadMifare(int iPort, ISO14443A_UID_T * card)
{
int ret;
unsigned int j,i;
int max_block,sector;
int type = 0x60; // 0x60 为密钥A 认证, 0x61 为密钥B认证
unsigned char data[256][16] = {0};
unsigned char key[6] = {0xff,0xff,0xff,0xff,0xff,0xff};
//read card
if(card->SAK == 0x18)
{
max_block = 256;
}
else {
max_block = 64;
}
for (i = 0, sector=-1 ;i< max_block;i++)
{
if(get_sector_num(i) != sector )
{//扇区发生变化,需要重新认证
//需要先HALT一下卡片,必要的,但暂不清楚原因
ISO14443A_Halt(iPort, 0);
sector = get_sector_num(i);
ret = MIFARE1_AuthBlock(iPort, card->UID, card->UIDLen, i, type, key, 0);
if (ret != 0x90){
printf("Mifare Auth block %d Failed!\n",i);
return -1;
}
}
ret = MIFARE1_ReadBlock(iPort, i, data[i], 0);
if (ret != 0x90) {
printf("Read block %d failed!\n",i);
return -1;
}
}
/* //write block
memset(data,rand(),16);
ISO14443A_Halt(iPort, 0);
MIFARE1_AuthBlock(iPort, uid[0].UID, uid[0].UIDLen, 1, type, key, 0);
ret = MIFARE1_WriteBlock(iPort,1,data,0);
if(ret != 0x90) {
printf("Write block 1 failed!\n");
return -1;
}
*/
//
for (i = 0;i< max_block && i<8;i++) //只显示一部分数据,否则内容太多刷屏了
{
for(j=0;j<16;j++) {
if(j==8) {
printf(" ");
}
printf("%02X ",data[i][j]);
}
printf("\n");
}
return max_block;
}
static unsigned char passport_data[128*1024];
static unsigned char passport_photo[256*1024];
void ReadPassport(int iPort,const char * pass_num, const char * birthday, const char * expire)
{
int ret;
FILE * fp;
//读护照
char auth[90] = {0};
char MRZ[256];
//生成认证码 auth
GetICAO9303AuthCode(pass_num,birthday,expire,auth);
//读取护照
ret = ReadICAO9303All(iPort,auth,passport_data,sizeof(passport_data));
if(ret<=0) {
return ;
}
ret = GetICAO9303MRZ(passport_data,MRZ,sizeof(MRZ));
if(ret<0) {
return ;
}
//读取护照/港澳通行证成功
printf("MRZ: %s\n",MRZ);
ret = GetICAO9303JpgData(passport_data,passport_photo,sizeof(passport_photo));
if(ret>0) {
fp = fopen("image.jpg","wb+");
if(fp) {
fwrite(passport_photo,1,ret,fp);
fclose(fp);
}
}
}
void ReadOtherCard(int iPort)
{
int ret;
unsigned int j,i,cnt = 0;
ISO14443A_UID_T uid[MAX_UID_FIND];
//find card
ret = ISO14443A_FindCard(iPort,uid,MAX_UID_FIND,&cnt,0);
if(ret != 0x90) {
goto finished;
}
if(cnt <= 0) {
goto finished;
}
for(i=0;i<cnt;i++) {
if(uid[i].SAK == 0x8) {
printf("Mifare S50:\t");
}
else if(uid[i].SAK == 0x18) {
printf("Mifare S70:\t");
}
else {
printf("Unknow:\t");
}
for(j=0;j<uid[i].UIDLen;j++) {
printf("%02X",uid[i].UID[j]);
}
printf("\n");
}
if(uid[0].SAK & 0x8) {
//尝试作为 Mifare IC卡读取
ret = ReadMifare(iPort,&uid[0]);
if(ret>=0) {
ISO14443A_Halt(iPort,0);
SDT_ReaderBeep(iPort,100,0);
Sleep(1000);
}
ISO14443A_ModeExit(iPort,0); //退出模式(短暂关闭天线),并Sleep一段时间,解决部分银行卡作为Mifare卡读过之后,就不能读取银行卡号的问题
Sleep(200);
}
if(uid[0].SAK & 0x20) {
//尝试作为银行卡读取
ret = ReadBankCard(iPort,&uid[0]);
if(ret>=0) {
SDT_ReaderBeep(iPort,100,0);
Sleep(1000);
goto finished;
}
}
if(uid[0].SAK & 0x20) {
//尝试作为护照/港澳通行证读取
ISO14443A_ModeExit(iPort,0);// 接口要求需要先退出type a模式
Sleep(10);
// 读取护照的参数需要通过OCR文字识别,获得护照(或港澳通行证)的号码,生日,过期时间, 并作为参数传入
// 所以此处传入的 "C06765690", "131116", "191125" 只是示例, 需要根据实际情况修改
ReadPassport(iPort,"C06765690","131116","191125");
}
finished:
// 从 ISO14443A 模式切回 ISO14443B模式(读取身份证模式),这样可以继续读取身份证,还起到了重置 ISO14443A 类型卡片的作用
ISO14443A_ModeExit(iPort,0);
Sleep(50);
}
int main(int argc, char * argv[])
{
int port = 1001;
int ret;
unsigned char ManaInfo[4];
unsigned char ManaMsg[8];
unsigned char text[256];
unsigned char image[1024];
unsigned char finger[1024];
unsigned char AppInfo[1024];
unsigned char bmp[65536];
unsigned int textLen,imageLen,fingerLen,AppInfoLen;
unsigned int ID[4];
char str[1024];
FILE * fp;
if(argc > 1) {
port = atoi(argv[1]);
}
#ifndef WIN32
print_com_list();
#endif
while(1) {
ret = SDT_GetSAMStatus(port,1);
if(ret != 0x90 ) {
Sleep(200);
continue;
}
/*
SDT.ResetSAM(port,1);
Sleep(1000);
continue;
*/
ret = SDT_OpenPort(port);
if(ret != 0x90 ) {
Sleep(200);
continue;
}
printf("reader detected!\n");
ret = SDT_GetSAMID(port,(unsigned char *)ID,0);
if(ret!= 0x90) {
SDT_ClosePort(port);
continue;
}
ret = SDT_GetSAMID(port,(unsigned char *)ID,0);
if(ret!= 0x90) {
SDT_ClosePort(port);
continue;
}
printf("SAMID: %02d.%02d-%08d-%010d-%010d\n",ID[0]&0xffff,ID[0]>>16,ID[1],ID[2],ID[3]);
ret = SDT_GetSAMIDToStr(port,str,0);
if(ret!= 0x90) {
SDT_ClosePort(port);
continue;
}
printf("SAMID: %s\n",str);
while(1) {
ret = SDT_ReadBaseFPMsg(port,text,&textLen,image,&imageLen,finger,&fingerLen,0);
if(ret == 0x90) {
printf("read card success!(%d,%d,%d)\n",textLen,imageLen,fingerLen);
goto success;
}
else if(ret < 0x21 ){
break;
}
ret = SDT_StartFindIDCard(port,ManaInfo,0);
if(ret == 0x9f) {
printf("find card success!\n");
}
else if(ret < 0x21 ){
break;
}
else {
//读取其他类型的卡片
ReadOtherCard(port);
Sleep(100);
continue;
}
ret = SDT_SelectIDCard(port,ManaMsg,0);
if(ret == 0x90) {
printf("select card success!\n");
}
else if(ret < 0x21 ){
break;
}
else {
printf("select failed!(ret=0x%02X)\n",ret);
Sleep(1000);
continue;
}
ret = SDT_ReadBaseFPMsg(port,text,&textLen,image,&imageLen,finger,&fingerLen,0);
if(ret == 0x90) {
printf("read card success!(%d,%d,%d)\n",textLen,imageLen,fingerLen);
goto success;
}
else if(ret < 0x21 ){
break;
}
else {
printf("read card failed!(ret=0x%02X)\n",ret);
// 读取护照的参数需要通过OCR文字识别,获得护照(或港澳通行证)的号码,生日,过期时间, 并作为参数传入
// 所以此处传入的 "E18314949", "870623", "240615" 只是示例, 需要根据实际情况修改
ReadPassport(port,"E18314949","870623","240615");//身份证读取失败了,尝试读取护照/港澳通行证
Sleep(1000);
continue;
}
success:
AppInfoLen = 0;
ret = SDT_ReadNewAppMsg(port,AppInfo,&AppInfoLen,0);
if(ret == 0x90){
printf("read address success!(%d)\n",AppInfoLen);
}
else {
printf("no address!(0x%x)\n",ret);
}
ret = SDT_ReadBaseFPMsgToFile(port,"text.txt",&textLen,"image.wlt",&imageLen,"finger.bin",&fingerLen,0);
print_info2((char*)text);
ret = GetBmpData(image,bmp,sizeof(bmp));
if(ret > 0 ) {
fp = fopen("image.bmp","wb+");
if(fp) {
fwrite(bmp,1,ret,fp);
fclose(fp);
}
}
Sleep(1000);
}
printf("reader removed!\n");
SDT_ClosePort(port);
}
return 0;
}
八、Delphi
object Form1: TForm1
Left = 212
Top = 147
Width = 1740
Height = 900
Caption = #27493#32852#31185#25216' '#36523#20221#35777#38405#35835#28436#31034#31243#24207
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
OnCreate = OnCreate
PixelsPerInch = 120
TextHeight = 16
object Label1: TLabel
Left = 16
Top = 32
Width = 48
Height = 16
Caption = #31471#21475#21495#65306
end
object Label2: TLabel
Left = 16
Top = 136
Width = 36
Height = 16
Caption = #22995#21517#65306
end
object Label3: TLabel
Left = 16
Top = 176
Width = 36
Height = 16
Caption = #24615#21035#65306
end
object Label4: TLabel
Left = 184
Top = 176
Width = 36
Height = 16
Caption = #27665#26063#65306
end
object Label5: TLabel
Left = 16
Top = 208
Width = 60
Height = 16
Caption = #20986#29983#26085#26399#65306
end
object Label6: TLabel
Left = 16
Top = 240
Width = 36
Height = 16
Caption = #20303#22336#65306
end
object Label7: TLabel
Left = 16
Top = 328
Width = 72
Height = 16
Caption = #36523#20221#35777#21495#30721#65306
end
object Label8: TLabel
Left = 16
Top = 368
Width = 60
Height = 16
Caption = #31614#21457#26426#20851#65306
end
object Label9: TLabel
Left = 16
Top = 400
Width = 60
Height = 16
Caption = #26377#25928#26399#38480#65306
end
object Label10: TLabel
Left = 16
Top = 72
Width = 54
Height = 16
Caption = 'SAMID'#65306
end
object Label11: TLabel
Left = 16
Top = 104
Width = 60
Height = 16
Caption = #21345#29255#24207#21495#65306
end
object Image1: TImage
Left = 296
Top = 104
Width = 105
Height = 105
end
object txtName: TEdit
Left = 136
Top = 136
Width = 137
Height = 24
ReadOnly = True
TabOrder = 0
end
object txtGender: TEdit
Left = 136
Top = 168
Width = 41
Height = 24
ReadOnly = True
TabOrder = 1
end
object btRead: TButton
Left = 392
Top = 32
Width = 75
Height = 25
Caption = #35835#21345
TabOrder = 2
OnClick = btReadClick
end
object btOpen: TButton
Left = 280
Top = 32
Width = 97
Height = 25
Caption = #25171#24320#31471#21475
TabOrder = 3
OnClick = btOpenClick
end
object cbPort: TComboBox
Left = 136
Top = 32
Width = 129
Height = 24
ItemHeight = 16
TabOrder = 4
Text = '0'
end
object txtBirthday: TEdit
Left = 136
Top = 200
Width = 137
Height = 24
ReadOnly = True
TabOrder = 5
end
object txtAddress: TEdit
Left = 136
Top = 232
Width = 209
Height = 24
ReadOnly = True
TabOrder = 6
end
object txtNumber: TEdit
Left = 136
Top = 328
Width = 209
Height = 24
ReadOnly = True
TabOrder = 7
end
object txtIssuer: TEdit
Left = 136
Top = 360
Width = 209
Height = 24
ReadOnly = True
TabOrder = 8
end
object txtValid: TEdit
Left = 136
Top = 392
Width = 209
Height = 24
ReadOnly = True
TabOrder = 9
end
object txtSAMID: TEdit
Left = 136
Top = 64
Width = 241
Height = 24
ReadOnly = True
TabOrder = 10
end
object StatusBar1: TStatusBar
Left = 0
Top = 834
Width = 1722
Height = 19
Panels = <
item
Width = 250
end
item
Width = 450
end>
end
object txtRace: TEdit
Left = 232
Top = 168
Width = 41
Height = 24
ReadOnly = True
TabOrder = 12
end
object txtCardSn: TEdit
Left = 136
Top = 104
Width = 137
Height = 24
ReadOnly = True
TabOrder = 13
end
end
九、阿雪技术观
拥抱开源与共享,见证科技进步奇迹,畅享人类幸福时光!
让我们积极投身于技术共享的浪潮中,不仅仅是作为受益者,更要成为贡献者。无论是分享自己的代码、撰写技术博客,还是参与开源项目的维护和改进,每一个小小的举动都可能成为推动技术进步的巨大力量