1、引入插件
在小程序的app.json中,加入插件。
"plugins": {
? ? "readcard-plugin": {
? ? ? "version": "2.3.2",
? ? ? "provider": "wxa2583ebacdb87a6a"
? ? }
}
2、基礎(chǔ)庫
調(diào)試基礎(chǔ)庫? 2.18.1及以上
微信版本:
iOS? ? ? 暫不支持NFC(微信官方未支持),可使用藍(lán)牙外設(shè)讀卡器
Android? 8.0.6及以上版本,同時(shí)支持NFC、藍(lán)牙外設(shè)讀卡器
3、使用插件功能
3.1、讀取身份證件信息功能
在需要使用身份證讀卡的頁面中,使用插件示例。
const plugin = requirePlugin('readcard-plugin');
var StatusCode = null;
var that = this;
var initSuccess = false;
Page({
? data: {
? ? idCardInfo: null,
? },
? onLoad() {
? ? that = this;
? ? StatusCode = plugin.StatusCodeEnum();
? ? console.log("對照狀態(tài)碼:", StatusCode);
? ? // 設(shè)置APPID (開始讀卡之前,必須設(shè)置)
? ? plugin.setAppId("appid聯(lián)系我司商務(wù)獲取");
? ? // 設(shè)置讀卡SDK參數(shù)
? ? that.readSetting();
? ? // 初始化讀卡
? ? that.initRead();
? },
? // 設(shè)置讀卡SDK參數(shù)
? readSetting(){
? ? // 日志保存目錄
? ? // 默認(rèn)在 Android/data/com.tencent.mm/MicroMsg/wxanewfiles目錄下? 搜索 zrgk_mini_log
? ? // 不需要日志時(shí),可以不設(shè)置
? ? plugin.setShowLog(wx.getFileSystemManager(), wx.env.USER_DATA_PATH);
? ? // 讀卡參數(shù)設(shè)置,根據(jù)需要自行修改參數(shù)值,參數(shù)名稱及類型不可修改。
? ? var _Setting = {
? ? ? decodeImageType: "dn1", // dn0: 無照片? dn1: 平臺解碼照片
? ? ? readCardType: 2, // 2:NFC? 7:藍(lán)牙外設(shè)讀卡器? 9: NFC讀護(hù)照/通行證
? ? ? saveLog: true, // 是否保存日志文件。設(shè)置true時(shí),必須調(diào)用以上的 plugin.setShowLog(); 設(shè)置日志保存的目錄
? ? ? openLocalCache: true, // 是否開啟本地緩存
? ? ? // 解碼服務(wù)器配置。
? ? ? // 默認(rèn)第1個(gè)為主服務(wù)器,優(yōu)先使用。
? ? ? // 其余皆為備用服務(wù)器,主服務(wù)器異常時(shí)自動切換啟用
? ? ? ipPortArray: [{
? ? ? ? address: 'yfs4.sfzydq.com',
? ? ? ? port: 9999,
? ? ? ? canUse: true
? ? ? }, {
? ? ? ? address: 'test.sfzydq.com',
? ? ? ? port: 18180,
? ? ? ? canUse: true
? ? ? }]
? ? };
? ? // 將參數(shù)設(shè)置給插件
? ? plugin.readSetting(_Setting);
? },
? // 初始化 讀卡示例代碼
? initRead() {
? ? if (initSuccess == true) {
? ? ? wx.showToast({
? ? ? ? title: '已經(jīng)初始化過了',
? ? ? ? icon: 'none'
? ? ? });
? ? ? return;
? ? }
? ? // 初始化并開始讀卡
? ? plugin.startReadCard(function (code, msg, value, cardType) {
? ? ? let code_msg = "code:" + code + "\n" + "msg:" + msg;
? ? ? that.setData({
? ? ? ? msg: code_msg,
? ? ? });
? ? ? switch (code) {
? ? ? ? case StatusCode.ININ_ING.code: // 初始化中
? ? ? ? ? wx.showLoading({
? ? ? ? ? ? title: '初始化中...',
? ? ? ? ? ? mask: true
? ? ? ? ? });
? ? ? ? ? break;
? ? ? ? case StatusCode.ININ_OK.code: // 初始化成功
? ? ? ? ? initSuccess = true;
? ? ? ? ? wx.hideLoading();
? ? ? ? ? wx.showToast({
? ? ? ? ? ? title: '初始化成功',
? ? ? ? ? ? icon: 'success'
? ? ? ? ? })
? ? ? ? ? break;
? ? ? ? case StatusCode.ININ_FAILE.code: // 初始化失敗
? ? ? ? ? initSuccess = false;
? ? ? ? ? wx.hideLoading();
? ? ? ? ? if (msg.indexOf("13000") != -1) {
? ? ? ? ? ? wx.showModal({
? ? ? ? ? ? ? title: '溫馨提示',
? ? ? ? ? ? ? content: '設(shè)備不支持NFC',
? ? ? ? ? ? ? complete: (res) => {}
? ? ? ? ? ? });
? ? ? ? ? } else if (msg.indexOf("13001") != -1) {
? ? ? ? ? ? wx.showModal({
? ? ? ? ? ? ? title: '溫馨提示',
? ? ? ? ? ? ? content: '系統(tǒng)NFC開關(guān)未打開',
? ? ? ? ? ? ? complete: (res) => {}
? ? ? ? ? ? });
? ? ? ? ? } else {
? ? ? ? ? ? wx.showToast({
? ? ? ? ? ? ? title: msg,
? ? ? ? ? ? ? icon: 'error'
? ? ? ? ? ? })
? ? ? ? ? }
? ? ? ? ? break;
? ? ? ? case StatusCode.FIND_CARD_START.code: // 開始尋卡
? ? ? ? ? wx.showToast({
? ? ? ? ? ? title: '開始尋卡',
? ? ? ? ? ? icon: 'success'
? ? ? ? ? })
? ? ? ? ? break;
? ? ? ? case StatusCode.FIND_CARD_SUCCESS.code: // 尋卡成功
? ? ? ? ? allCount += 1;
? ? ? ? ? wx.showToast({
? ? ? ? ? ? title: '尋卡成功',
? ? ? ? ? ? icon: 'success'
? ? ? ? ? })
? ? ? ? ? break;
? ? ? ? case StatusCode.READCARD_START.code: // 開始解碼
? ? ? ? ? wx.showLoading({
? ? ? ? ? ? title: '請勿移動卡片',
? ? ? ? ? ? mask: true
? ? ? ? ? })
? ? ? ? ? break;
? ? ? ? case StatusCode.READCARD_SUCCESS.code: // 解碼成功
? ? ? ? ? wx.hideLoading();
? ? ? ? ? wx.showToast({
? ? ? ? ? ? title: '解碼成功',
? ? ? ? ? ? icon: 'success'
? ? ? ? ? })
? ? ? ? ? switch (cardType) {
? ? ? ? ? ? case StatusCode.CARD_IC.code: // IC卡
? ? ? ? ? ? ? console.log("IC卡:" , value);
? ? ? ? ? ? ? break;
? ? ? ? ? ? case StatusCode.CARD_LCT_STUDENT.code: // 綠城通學(xué)生卡
? ? ? ? ? ? ? console.log("綠城通學(xué)生卡:" , value);
? ? ? ? ? ? ? break;
? ? ? ? ? ? case StatusCode.CARD_LCT_NORMAL.code: // 綠城通普通卡
? ? ? ? ? ? ? console.log("綠城通普通卡:" , value);
? ? ? ? ? ? ? break;
? ? ? ? ? ? case StatusCode.CARD_LCT_OLD.code: // 綠城通老年卡
? ? ? ? ? ? ? console.log("綠城通老年卡:" , value);
? ? ? ? ? ? ? break;
? ? ? ? ? ? case StatusCode.CARD_IDCARD.code: // 身份證
? ? ? ? ? ? ? // 讀取到的身份證信息
? ? ? ? ? ? ? // 詳細(xì)字段說明,詳見文檔下發(fā)的附錄
? ? ? ? ? ? ? var idCardInfo = JSON.parse(value);
? ? ? ? ? ? ? console.log("身份證信息:" , idCardInfo);
? ? ? ? ? ? ? // 當(dāng)設(shè)置平臺解碼身份證照片時(shí),
? ? ? ? ? ? ? // idCardInfo.image字段,為base64編碼的照片字符串,可直接用于顯示
? ? ? ? ? ? ? // idCardInfo.type=1080 中國居民身份證
? ? ? ? ? ? ? // idCardInfo.type=1081 新版外國永久居住證
? ? ? ? ? ? ? // idCardInfo.type=1082 港澳臺居住證
? ? ? ? ? ? ? // idCardInfo.type=1083 舊版外國永久居住證
? ? ? ? ? ? ? break;
? ? ? ? ? ? case StatusCode.CARD_EPASSPORT.code: // 護(hù)照/通行證
? ? ? ? ? ? ? // 讀取到的護(hù)照/通行證信息
? ? ? ? ? ? ? // 詳細(xì)字段說明,詳見文檔下發(fā)的附錄
? ? ? ? ? ? ? var cardInfo = JSON.parse(value);
? ? ? ? ? ? ? console.log("證件信息:" , cardInfo);
? ? ? ? ? ? ? // cardInfo.image字段,為base64編碼的照片字符串,可直接用于顯示
? ? ? ? ? ? ? break;
? ? ? ? ? }
? ? ? ? ? break;
? ? ? ? case StatusCode.READCARD_FAILE.code: // 解碼失敗
? ? ? ? ? wx.hideLoading();
? ? ? ? ? wx.showToast({
? ? ? ? ? ? title: '解碼失敗' + msg,
? ? ? ? ? ? icon: 'error'
? ? ? ? ? })
? ? ? ? ? that.readFa();
? ? ? ? ? break;
? ? ? }
? ? });
? },
? onUnload() {
? ? console.log("onUnload", "界面銷毀,需要停止讀卡");
? ? plugin.stopReadCard();
? }
})
3.2、活體檢測/人臉比對功能
在界面的 **.wxml 添加camera
<camera device-position="front" flash="off" resolution='medium' frame-size='small' style="height: 100%;width: 100%;"/>
界面的其余樣式,自行定義
在界面 **.js 配置使用插件
const plugin = requirePlugin('readcard-plugin');
var that = this;
Page({
? /**
? * 頁面的初始數(shù)據(jù)
? */
? data: {
? ? type: 1, // 1:僅活體檢測? 2:活體 + 人臉1:1比對
? ? outTimeMs: 5000, // 超時(shí)時(shí)間 毫秒(最小3秒)
? ? photoData: null, // (照片數(shù)據(jù)的字節(jié)數(shù)組)僅活體檢測不需要傳比對的照片,人臉人臉1:1比對時(shí),需要傳入比對的照片
? ? appid: "", // appid聯(lián)系我司商務(wù)獲取
? ? _Setting: {
? ? ? saveLog: true, // 是否保存日志文件
? ? ? // 服務(wù)器配置
? ? ? ipPortArray: [{
? ? ? ? address: 'test.sfzydq.com',
? ? ? ? port: 18181,
? ? ? ? canUse: true
? ? ? }]
? ? }
? },
? /**
? * 生命周期函數(shù)--監(jiān)聽頁面加載
? */
? onLoad(options) {
? ? that = this;
? ? that.authorize();
? },
? /**
? * 向用戶申請相機(jī)權(quán)限
? */
? authorize() {
? ? wx.getSetting({
? ? ? success(res) {
? ? ? ? if (!res.authSetting['scope.camera']) {
? ? ? ? ? wx.authorize({
? ? ? ? ? ? scope: 'scope.camera',
? ? ? ? ? ? success() {
? ? ? ? ? ? ? // 用戶已經(jīng)同意
? ? ? ? ? ? ? that.faceLive();
? ? ? ? ? ? },
? ? ? ? ? ? fail() {
? ? ? ? ? ? ? wx.openSetting({
? ? ? ? ? ? ? ? success(res) {
? ? ? ? ? ? ? ? ? that.authorize();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? })
? ? ? ? ? ? }
? ? ? ? ? })
? ? ? ? } else {
? ? ? ? ? that.faceLive();
? ? ? ? }
? ? ? }
? ? })
? },
? /**
? * 開始活體檢測
? */
? faceLive() {
? ? // 設(shè)置APPID
? ? plugin.setAppId(that.data.appid);
? ? // 設(shè)置其他檢測參數(shù)
? ? plugin.settingFacelive(that.data._Setting);
? ? // 幀數(shù)據(jù)回調(diào)
? ? const context = wx.createCameraContext()
? ? const listener = context.onCameraFrame((frame) => {
? ? ? plugin.onCameraFrame(frame);
? ? });
? ? // 開始檢測
? ? plugin.startFacelive(listener, that.data.type, that.data.outTimeMs, that.data.photoData)
? ? ? .then((bean) => {
? ? ? ? console.log(bean);
? ? ? ? var content = "";
? ? ? ? if (bean.code != "00") { // 失敗
? ? ? ? ? content = bean.msg;
? ? ? ? } else {
? ? ? ? ? content = "結(jié)果:" + bean.code + "\n活體分值:" + bean.liveGrade;
? ? ? ? ? if (that.data.type == 2) {
? ? ? ? ? ? content += "\n人臉比對分值:" + bean.faceGrade;
? ? ? ? ? }
? ? ? ? }
? ? ? ? wx.showModal({
? ? ? ? ? title: '溫馨提示',
? ? ? ? ? content: content,
? ? ? ? ? showCancel: false,
? ? ? ? ? success: (res) => {
? ? ? ? ? ? if (res.confirm) { // 用戶點(diǎn)擊確定
? ? ? ? ? ? ? wx.navigateBack();
? ? ? ? ? ? }
? ? ? ? ? }
? ? ? ? });
? ? ? }).catch((err) => {
? ? ? ? console.log(err);
? ? ? ? var content = "";
? ? ? ? try {
? ? ? ? ? if (typeof err == "object") {
? ? ? ? ? ? let bean = err;
? ? ? ? ? ? content = bean.msg;
? ? ? ? ? ? content += "\n活體分值:" + bean.liveGrade;
? ? ? ? ? ? if (that.data.type == 2) {
? ? ? ? ? ? ? content += "\n人臉比對分值:" + bean.faceGrade;
? ? ? ? ? ? }
? ? ? ? ? } else {
? ? ? ? ? ? content = err;
? ? ? ? ? }
? ? ? ? } catch (error) {
? ? ? ? ? content = JSON.stringify(error);
? ? ? ? }
? ? ? ? wx.showModal({
? ? ? ? ? title: '溫馨提示',
? ? ? ? ? content: content,
? ? ? ? ? showCancel: false,
? ? ? ? ? success: (res) => {
? ? ? ? ? ? if (res.confirm) { // 用戶點(diǎn)擊確定
? ? ? ? ? ? ? wx.navigateBack();
? ? ? ? ? ? }
? ? ? ? ? }
? ? ? ? });
? ? ? });
? },
? /**
? * 生命周期函數(shù)--監(jiān)聽頁面卸載
? */
? onUnload() {
? ? plugin.stopFacelive();
? }
})
4、配置TCP合法域名
必須將參數(shù)配置時(shí),配置的 ipPortArray 參數(shù)中的所有服務(wù)器地址,加入到合法的TCP域名,
否則無法訪問到服務(wù)器。
如配置:
tcp://yfs4.sfzydq.com
tcp://test.sfzydq.com
tcp://yjm2.sfzydq.com
tcp://yfs3.sfzydq.com
5、附錄
身份證詳細(xì)信息IdCardData:
字段? ? ? ? ? ? ? ? ? ? ? ? ?類型? ? ? ? ? ? ? ? ? ? ? ? 注釋
type? ? ? ? ? ? ? ? ? ? ? ? ? ?int? ? ? ? ? ? ? ? ? ? ? ? ? 1080:中國居民身份證
address? ? ? ? ? ? ? ? ? ?string? ? ? ? ? ? ? ? ? ? ? ? 家庭住址
birthday? ? ? ? ? ? ? ? ? ?string? ? ? ? ? ? ? ? ? ? ? ? 出生日期
dn? ? ? ? ? ? ? ? ? ? ? ? ? ? string? ? ? ? ? ? ? ? ? ? ? ? dn碼
endDate? ? ? ? ? ? ? ? ? string? ? ? ? ? ? ? ? ? ? ? ? ?身份證結(jié)束時(shí)間
startDate? ? ? ? ? ? ? ? ?string? ? ? ? ? ? ? ? ? ? ? ? ?身份證生效時(shí)間
id? ? ? ? ? ? ? ? ? ? ? ? ? ? ?string? ? ? ? ? ? ? ? ? ? ? ? ? 身份證號碼
image? ? ? ? ? ? ? ? ? ? ? string? ? ? ? ? ? ? ? ? ? ? ? ? base64照片
issue? ? ? ? ? ? ? ? ? ? ? ?string? ? ? ? ? ? ? ? ? ? ? ? ? ?簽發(fā)機(jī)關(guān)
name? ? ? ? ? ? ? ? ? ? ? string? ? ? ? ? ? ? ? ? ? ? ? ? ? 姓名
nation? ? ? ? ? ? ? ? ? ? ?string? ? ? ? ? ? ? ? ? ? ? ? ? ? 民族
sex? ? ? ? ? ? ? ? ? ? ? ? ? string? ? ? ? ? ? ? ? ? ? ? ? ? ? 性別
uuid? ? ? ? ? ? ? ? ? ? ? ? ?string? ? ? ? ? ? ? ? ? ? ? ? ? ?uuid
外國人永久居住證IdCardData:
字段? ? ? ? ? ? ? ? ? ? ? ? ?類型? ? ? ? ? ? ? ? ? ? ? ? ? ? ?注釋
type? ? ? ? ? ? ? ? ? ? ? ? ?int? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?1081:新版外國人永久居住證 1083:舊版外國人永久居住證
birthday? ? ? ? ? ? ? ? ? ?string? ? ? ? ? ? ? ? ? ? ? ? ? ?出生日期
dn? ? ? ? ? ? ? ? ? ? ? ? ? ? string? ? ? ? ? ? ? ? ? ? ? ? ? ?dn碼
endDate? ? ? ? ? ? ? ? ? ?string? ? ? ? ? ? ? ? ? ? ? ? ? 身份證結(jié)束時(shí)間
startDate? ? ? ? ? ? ? ? ? string? ? ? ? ? ? ? ? ? ? ? ? ? 身份證生效時(shí)間
id? ? ? ? ? ? ? ? ? ? ? ? ? ? ? string? ? ? ? ? ? ? ? ? ? ? ? ? 身份證號碼
issue? ? ? ? ? ? ? ? ? ? ? ? string? ? ? ? ? ? ? ? ? ? ? ? ? ?簽發(fā)機(jī)關(guān)
sex? ? ? ? ? ? ? ? ? ? ? ? ? ?string? ? ? ? ? ? ? ? ? ? ? ? ? ?性別
uuid? ? ? ? ? ? ? ? ? ? ? ? ? string? ? ? ? ? ? ? ? ? ? ? ? ? ?uuid
image? ? ? ? ? ? ? ? ? ? ? ?string? ? ? ? ? ? ? ? ? ? ? ? ? ?base64照片
nameChinese? ? ? ? ? ?string? ? ? ? ? ? ? ? ? ? ? ? ? ?中文姓名
nameEnglish? ? ? ? ? ? ?string? ? ? ? ? ? ? ? ? ? ? ? ? ?英文姓名
nameEnglishAdd? ? ? ?string? ? ? ? ? ? ? ? ? ? ? ? ? 英文姓名備用
nationlity? ? ? ? ? ? ? ? ? ? ?string? ? ? ? ? ? ? ? ? ? ? ? ? 國籍
cardVersion? ? ? ? ? ? ? ? ?string? ? ? ? ? ? ? ? ? ? ? ? ?卡版本號
cardType? ? ? ? ? ? ? ? ? ? ?string? ? ? ? ? ? ? ? ? ? ? ? ? 卡類型
historicalNumber? ? ? ? ? string? ? ? ? ? ? ? ? ? ? ? ? ? 既往版本證件號碼關(guān)聯(lián)項(xiàng)
renewalNumber? ? ? ? ? ?string? ? ? ? ? ? ? ? ? ? ? ? ? ?換證次數(shù)
港澳臺居住證詳細(xì)信息IdCardData:
字段? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 類型? ? ? ? ? ? ? ? ? ? ? ? ? ? ?注釋
type? ? ? ? ? ? ? ? ? ? ? ? ? ? ? int? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 1082:港澳臺居住證
address? ? ? ? ? ? ? ? ? ? ? ? string? ? ? ? ? ? ? ? ? ? ? ? ? ? 家庭住址
birthday? ? ? ? ? ? ? ? ? ? ? ? string? ? ? ? ? ? ? ? ? ? ? ? ? ? 出生日期
dn? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? string? ? ? ? ? ? ? ? ? ? ? ? ? ? dn碼
endDate? ? ? ? ? ? ? ? ? ? ? ? string? ? ? ? ? ? ? ? ? ? ? ? ? ? 身份證結(jié)束時(shí)間
startDate? ? ? ? ? ? ? ? ? ? ? ?string? ? ? ? ? ? ? ? ? ? ? ? ? ? 身份證生效時(shí)間
id? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?string? ? ? ? ? ? ? ? ? ? ? ? ? ? 身份證號碼
image? ? ? ? ? ? ? ? ? ? ? ? ? ? string? ? ? ? ? ? ? ? ? ? ? ? ? ? base64照片
issue? ? ? ? ? ? ? ? ? ? ? ? ? ? ?string? ? ? ? ? ? ? ? ? ? ? ? ? ? 簽發(fā)機(jī)關(guān)
name? ? ? ? ? ? ? ? ? ? ? ? ? ? ?string? ? ? ? ? ? ? ? ? ? ? ? ? ? 姓名
nation? ? ? ? ? ? ? ? ? ? ? ? ? ? string? ? ? ? ? ? ? ? ? ? ? ? ? ? 民族
sex? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?string? ? ? ? ? ? ? ? ? ? ? ? ? ? 性別
uuid? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? string? ? ? ? ? ? ? ? ? ? ? ? ? ? uuid
passCheckId? ? ? ? ? ? ? ? ? string? ? ? ? ? ? ? ? ? ? ? ? ? ?通行證號碼
issuesNumber? ? ? ? ? ? ? ? string? ? ? ? ? ? ? ? ? ? ? ? ? ?簽發(fā)次數(shù)
護(hù)照通行證詳細(xì)信息:
字段? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 類型? ? ? ? ? ? ? ? ? ? ? ? ? ? ?注釋
nameOfHolder? ? ? ? ? ? ? ? string? ? ? ? ? ? ? ? ? ? ? ? ? 中文姓名
primaryIdentifier? ? ? ? ? ? ? string? ? ? ? ? ? ? ? ? ? ? ? ? ?姓
secondaryIdentifier? ? ? ? ?string? ? ? ? ? ? ? ? ? ? ? ? ? ? 名
gender? ? ? ? ? ? ? ? ? ? ? ? ? ? string? ? ? ? ? ? ? ? ? ? ? ? ? ? 性別 M男 F女
dateOfBirth? ? ? ? ? ? ? ? ? ? ? string? ? ? ? ? ? ? ? ? ? ? ? ? ?生日
nationality? ? ? ? ? ? ? ? ? ? ? ? string? ? ? ? ? ? ? ? ? ? ? ? ? ?國家碼
dateOfExpiry? ? ? ? ? ? ? ? ? ?string? ? ? ? ? ? ? ? ? ? ? ? ? ?有效期
documentNumber? ? ? ? ? ?string? ? ? ? ? ? ? ? ? ? ? ? ? ? 護(hù)照號碼
duration? ? ? ? ? ? ? ? ? ? ? ? ? long? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?解碼時(shí)長
mrz? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? string? ? ? ? ? ? ? ? ? ? ? ? ? ? ?機(jī)讀碼
personNumbe? ? ? ? ? ? ? ? ? rstring? ? ? ? ? ? ? ? ? ? ? ? ? ?個(gè)人號碼
idNumber? ? ? ? ? ? ? ? ? ? ? ? string? ? ? ? ? ? ? ? ? ? ? ? ? ? ?證件號碼
image? ? ? ? ? ? ? ? ? ? ? ? ? ? ? string? ? ? ? ? ? ? ? ? ? ? ? ? ? ?base64照片