应用笔记-Qt 程序中使用 Live 20R 指纹采集器
最近一个项目需要用到指纹认证。就在某东上随便搜了个销量比较大的指纹采集器,就是今天这个笔记的主角(Live 20R 指纹采集器)。花了几天时间研究明白了这个小东西是如何使用的。
Live 20R 指纹采集器本身提供了 SDK 开发包,叫做 ZKFinger SDK,我使用的版本是 5.0。这个 SDK 里提供了 ActiveX 接口和 C 语言接口。比较这两个接口的功能会发现 ActiveX 接口提供的功能更强,因此,应该优先使用这个接口。
不过我当时没仔细对比,直接就用了 SDK 中提供的 C 语言接口了。这个功能少了点,但是也够我用了。
简单的说,Live 20R 指纹采集器本身只是个指纹图像获取设备。它可以用来获取指纹图像。其他的功能都是 SDK 软件来实现的。因此我将功能划分到了两个 C++类中:
- FingerprintReader
- FingerprintCacheDB
FingerprintReader 用于获取指纹。FingerprintCacheDB 实现指纹比对等其他功能。另外还有两个辅助类:
- FingerprintEnroll
- FPCacheDBLoader
FingerprintEnroll 是个辅助类,用于简化指纹登记的代码。FPCacheDBLoader 用于将指纹存入数据库或从数据库中读出。
FingerprintReader
FingerprintReader 这个类直接与硬件打交道。用于获取指纹。里面用到了一个线程,不停的询问Live 20R 指纹采集器是否有新的指纹,如果有则发出一个 Qt 信号 :void onFingerprintReceived(QImage image, QByteArray fpTemplate)。
QByteArray fpTemplate 是从指纹中提取的信息。后面的比对都是用的这个 fpTemplate。指纹图像在后面倒是没什么用的。这里之所以把指纹图像给传出来了,主要是为了在界面上显示出来,看起来比较的酷。
下面直接给代码吧,首先是头文件:
#ifndef FINGERPRINTREADER_H
#define FINGERPRINTREADER_H
#include <QObject>
#include <QThread>
#include <QImage>
#include <QString>
#include <windows.h>
namespace ZKFP
{
/**
* @brief The FingerprintReader class 这个类完成指纹采集的工作。指纹判别工作由其他类来完成。
*/
class FingerprintReader : public QThread
{
Q_OBJECT
public:
explicit FingerprintReader(QObject *parent = nullptr);
~FingerprintReader();
/**
* @brief getDeviceCount 获取系统中识别出来的指纹识别器的数量
* @return
*/
int getDeviceCount();
QImage acquireImage();
QString vendorName() const;
QString productName() const;
QString serialNumber() const;
uint16_t getVID() const;
uint16_t getPID() const;
bool isFakeFuncOn();
QSize imageSize() {return QSize(m_image_width, m_image_height);}
signals:
/**
* @brief onFeatureInfo 取得指纹初始特征时发出该信号
* @param quality 表示该指纹特征的质量
* 0: 好的指纹特征, 1: 特征点不够, 2: 其它原因导致不能取到指纹特征, -1: 可疑
*/
void onFeatureInfo(int quality);
/**
* @brief onFingerTouching 当手指按压纹取像设备时发送该信号。
*/
void onFingerTouching();
/**
* @brief onFingerLeaving 当手离开压纹取像设备时发送该信号。
*/
void onFingerLeaving();
void onCapture(bool result);
void onFingerprintReceived(QImage image, QByteArray fpTemplate);
void fakeFinger();
public slots:
bool openDevice(int index = 0);
void closeDevice();
void beginCapture();
void cancelCapture();
/**
* @brief setFakeFunc 设置防假开关
* @param on true 表示开启防假指纹功能。 false 表示关闭防假指纹功能。
*/
void setFakeFunc(bool on);
private:
HANDLE m_handle;
QString m_vendorName;
QString m_productName;
QString m_serialNumber;
QImage m_fpImage;
int m_image_width;
int m_image_height;
int m_image_size;
int m_image_DPI;
int m_isFakeOn;
uint16_t m_vid;
uint16_t m_pid;
bool m_init;
void run() override;
bool isRealFinger();
bool m_stop;
};
}
#endif // FINGERPRINTREADER_H
接下来是 cpp 文件:
#include "FingerprintReader.h"
#include "FingerprintCacheDB.h"
#include "ZKFingerReader/libs/include/libzkfp.h"
#include <QDebug>
namespace ZKFP
{
static const int ZKFP_IMAGE_WIDTH_RO = 1;
static const int ZKFP_IMAGE_HEIGHT_RO = 2;
static const int ZKFP_IMAGE_DPI_RW = 3;
static const int ZKFP_IMAGE_SIZE_RO = 106;
static const int ZKFP_VID_PID_RO = 1015;
static const int ZKFP_FAKE_ON_RW = 2002;
static const int ZKFP_REAL_FINGER_RO = 2004;
static const int ZKFP_VENDOR_NAME_RO = 1101;
static const int ZKFP_PRODUCT_NAME = 1102;
static const int ZKFP_SERIAL_NUNBER = 1103;
//static const int MAX_TEMPLATE_SIZE = 2048;
static const int ZKFP_ERR_OK = 0;
int autoInitCount = 0;
static bool autoInit()
{
if(autoInitCount == 0)
{
if( ZKFPM_Init() == 0)
{
autoInitCount ++;
return true;
}
else
{
return false;
}
}
return true;
}
static bool autoTerminate()
{
autoInitCount --;
if(autoInitCount == 0)
{
return (ZKFPM_Terminate() == 0);
}
return false;
}
FingerprintReader::FingerprintReader(QObject *parent)
: QThread(parent),
m_init(false),
m_handle(0),
m_image_height(0),
m_image_width(0),
m_image_size(0),
m_vendorName(""),
m_productName(""),
m_serialNumber(""),
m_stop(true)
{
m_init = autoInit();
qDebug() << "autoInit = " << m_init;
}
int FingerprintReader::getDeviceCount()
{
return ZKFPM_GetDeviceCount();
}
bool FingerprintReader::openDevice(int index)
{
m_handle = ZKFPM_OpenDevice(index);
if(m_handle != 0)
{
setFakeFunc(true);
unsigned int length = sizeof(int);
m_image_DPI = 500;
length = sizeof(int);
ZKFPM_SetParameters(m_handle, ZKFP_IMAGE_DPI_RW, reinterpret_cast<unsigned char *> (&m_image_DPI), length);
length = sizeof(int);
ZKFPM_GetParameters(m_handle, ZKFP_IMAGE_DPI_RW, reinterpret_cast<unsigned char *> (&m_image_DPI), &length);
ZKFPM_GetParameters(m_handle, ZKFP_IMAGE_WIDTH_RO, reinterpret_cast<unsigned char *> (&m_image_width), &length);
length = sizeof(int);
ZKFPM_GetParameters(m_handle, ZKFP_IMAGE_HEIGHT_RO, reinterpret_cast<unsigned char *> (&m_image_height), &length);
length = sizeof(int);
ZKFPM_GetParameters(m_handle, ZKFP_IMAGE_SIZE_RO, reinterpret_cast<unsigned char *> (&m_image_size), &length);
qDebug() << "image width = " << m_image_width << ", height = " << m_image_height << ", DPI = " << m_image_DPI;
char name[128] = {0};
memset(name, 0, 128);
length = 128;
ZKFPM_GetParameters(m_handle, ZKFP_VENDOR_NAME_RO, reinterpret_cast<unsigned char *>(name), &length);
m_vendorName.clear();
m_vendorName.append(name);
memset(name, 0, 128);
length = 128;
ZKFPM_GetParameters(m_handle, ZKFP_PRODUCT_NAME, reinterpret_cast<unsigned char *>(name), &length);
m_productName.clear();
m_productName.append(name);
memset(name, 0, 128);
length = 128;
ZKFPM_GetParameters(m_handle, ZKFP_SERIAL_NUNBER, reinterpret_cast<unsigned char *>(name), &length);
m_serialNumber.clear();
m_serialNumber.append(name);
qDebug() << "Vendor Name = " << m_vendorName;
qDebug() << "Product Name = " << m_productName;
qDebug() << "Serial Number = " << m_serialNumber;
uint16_t vid_pid[2] = {0, 0};
length = 4;
ZKFPM_GetParameters(m_handle, ZKFP_VID_PID_RO, reinterpret_cast<unsigned char *>(vid_pid), &length);
m_vid = vid_pid[0];
m_pid = vid_pid[1];
qDebug() << "VID = " << m_vid << ", PID = " << m_pid;
return true;
}
else
{
m_vendorName.clear();
m_productName.clear();
m_serialNumber.clear();
m_vid = 0;
m_pid = 0;
}
return false;
}
bool FingerprintReader::isFakeFuncOn()
{
m_isFakeOn = 0;
unsigned int length = sizeof(m_isFakeOn);
ZKFPM_GetParameters(m_handle, ZKFP_FAKE_ON_RW, reinterpret_cast<unsigned char *>(&m_isFakeOn), &length);
return m_isFakeOn;
}
void FingerprintReader::setFakeFunc(bool on)
{
int fakeON = on ? 1 : 0;
unsigned int length = sizeof(m_isFakeOn);
ZKFPM_SetParameters(m_handle, ZKFP_FAKE_ON_RW, reinterpret_cast<unsigned char *> (&fakeON), sizeof(int));
ZKFPM_GetParameters(m_handle, ZKFP_FAKE_ON_RW, reinterpret_cast<unsigned char *>(&m_isFakeOn), &length);
}
void FingerprintReader::closeDevice()
{
if(isRunning())
{
cancelCapture();
}
if(m_handle)
{
ZKFPM_CloseDevice(m_handle);
m_vendorName.clear();
m_productName.clear();
m_serialNumber.clear();
m_image_height = 0;
m_image_width = 0;
m_image_size = 0;
m_vid = 0;
m_pid = 0;
m_handle = 0;
}
}
QImage FingerprintReader::acquireImage()
{
int width, height;
uint length = 4;
ZKFPM_GetParameters(m_handle, 1, (unsigned char*)&width, &length);
ZKFPM_GetParameters(m_handle, 2, (unsigned char*)&height, &length);
QImage image(width, height, QImage::Format_Grayscale8);
ZKFPM_AcquireFingerprintImage(m_handle, image.bits(), width * height);
return image;
}
QString FingerprintReader::vendorName() const
{
return m_vendorName;
}
QString FingerprintReader::productName() const
{
return m_productName;
}
QString FingerprintReader::serialNumber() const
{
return m_serialNumber;
}
uint16_t FingerprintReader::getVID() const
{
return m_vid;
}
uint16_t FingerprintReader::getPID() const
{
return m_pid;
}
bool FingerprintReader::isRealFinger()
{
int nFakeStatus = 0;
unsigned int retLen = sizeof(int);
if (0 == ZKFPM_GetParameters(m_handle, ZKFP_REAL_FINGER_RO, (unsigned char*)&nFakeStatus, &retLen))
{
if ((nFakeStatus & 31) == 31)
{
return true;
}
}
return false;
}
void FingerprintReader::run()
{
qDebug() << "Fingerprint sensor thead starting";
unsigned char * imageBuffer = new unsigned char[m_image_height * m_image_width + 100];
QImage image(m_image_width, m_image_height, QImage::Format_Grayscale8); //image 要求数据是 32位对齐的。
unsigned char fpBuffer[MAX_TEMPLATE_SIZE];
unsigned int tempLen = MAX_TEMPLATE_SIZE;
while(!m_stop)
{
tempLen = MAX_TEMPLATE_SIZE;
int ret = ZKFPM_AcquireFingerprint(m_handle, imageBuffer, m_image_size, fpBuffer, &tempLen);
//qDebug() << "AcquireFingerprint = " << ret;
if (0 == ret)
{
qDebug() << "AcquireFingerprint = " << ret;
qDebug() << "Template size = " << tempLen;
if ( m_isFakeOn && !isRealFinger() ) //FakeFinger Test
{
emit fakeFinger();
qDebug() << "fakeFinger";
}
else
{
for(int i = 0; i < m_image_height; i++)
{
unsigned char * to = image.scanLine(i);
unsigned char * from = imageBuffer + i * m_image_width;
memcpy(to, from, m_image_width);
}
QByteArray pfTemplate(reinterpret_cast<char *>(fpBuffer), tempLen);
//fpTemplate.truncate(tempLen);
emit onFingerprintReceived(image, pfTemplate);
qDebug() << "emit onImageReceived(image, fpTemplate)";
}
}
Sleep(100);
}
qDebug() << "Fingerprint sensor thead stoped";
}
void FingerprintReader::beginCapture()
{
if(m_init && m_handle)
{
m_stop = false;
}
start();
}
void FingerprintReader::cancelCapture()
{
m_stop = true;
}
FingerprintReader::~FingerprintReader()
{
if(m_handle)
{
closeDevice();
}
autoTerminate();
}
}
FingerprintCacheDB
FingerprintCacheDB 类用于实现指纹的比对。 SDK 中提供的功能包括 1 对 1 的比对,和 1 对 N 的识别。所谓 1 对 N 指的是提前在 FingerprintCacheDB 类里加载 N 个指纹数据。然后提供一个新的指纹,判断这个指纹是否和那 N 个指纹中的某一个是同一个指纹。
下面还是直接给代码吧,首先是头文件:
#ifndef FINGERPRINTCACHEDB_H
#define FINGERPRINTCACHEDB_H
#include <QObject>
#include <windows.h>
namespace ZKFP
{
QString BlobToBase64(QByteArray regTemplate);
QByteArray Base64ToBlob(QString base64String);
/**
* @brief The FingerprintCacheDB class 指纹识别类,用来进行 1:N 的指纹识别。
*
*/
class FingerprintCacheDB : public QObject
{
Q_OBJECT
public:
FingerprintCacheDB(QObject *parent = nullptr);
~FingerprintCacheDB();
/**
* @brief verifyFinger 比较两个指纹是否是同一个手指的
* @param regTemplate 指纹 1
* @param verTemplate 指纹 2
* @return 0 - 100 分数越高表明匹配度越好
*/
int verifyFinger(QByteArray regTemplate, QByteArray verTemplate);
/**
* @brief mergeFinger 将三个指纹模板合并成一个
* @param inTemplate1
* @param inTemplate2
* @param inTemplate3
* @param outTemplate
* @return
*/
int mergeFinger(QByteArray inTemplate1, QByteArray inTemplate2, QByteArray inTemplate3, QByteArray &outTemplate);
int identify(QByteArray inTemplate, unsigned int &fingerID, unsigned int &score);
int identify(QString base64String, unsigned int &fingerID, unsigned int &score);
/**
* @brief count 返回指纹数据库中的数据条数
* @return
*/
int count() const;
public slots:
/**
* @brief clearAll 清空指纹数据库
* @return
*/
int clearAll();
/**
* @brief deleteItem 删除 fingerID 对应的数据
* @param fingerID
* @return
*/
int deleteItem(unsigned int fingerID);
/**
* @brief addItem 将一个指纹模板登记到指纹数据库中,同时设置这个指纹模板的 ID 为 fingerID.
* @param fingerID
* @param regTemplate
* @return
*/
int addItem(unsigned int fingerID, QByteArray regTemplate);
int addItem(unsigned int fingerID, QString base64String);
private:
HANDLE m_hDBCache;//缓存区句柄
};
}
#endif // FINGERPRINTCACHEDB_H
然后是 cpp 文件:
#include "FingerprintCacheDB.h"
#include "ZKFingerReader/libs/include/libzkfp.h"
#include <QDebug>
namespace ZKFP
{
QString BlobToBase64(QByteArray regTemplate)
{
int size = (regTemplate.size() * 8 + 5 )/ 6;
char * str = new char[size + 1];
memset(str, 0, size + 1);
ZKFPM_BlobToBase64(reinterpret_cast<unsigned char *>(regTemplate.data()), regTemplate.size(),
str, size + 1);
QString ret(str);
// QString ret2 = regTemplate.toBase64();
// if(ret == ret2)
// {
// qDebug() << "base64 is same";
// }
delete []str;
return ret;
}
QByteArray Base64ToBlob(QString base64String)
{
int size = base64String.size() * 6 / 8;
unsigned char *blob = new unsigned char[size];
int length = ZKFPM_Base64ToBlob(reinterpret_cast<char *> (base64String.data()), blob, size);
QByteArray ret(reinterpret_cast<char*> (blob), length);
delete [] blob;
return ret;
}
FingerprintCacheDB::FingerprintCacheDB(QObject *parent)
:QObject(parent),
m_hDBCache(0)
{
m_hDBCache = ZKFPM_DBInit();
}
FingerprintCacheDB::~FingerprintCacheDB()
{
ZKFPM_DBFree(m_hDBCache);
}
int FingerprintCacheDB::clearAll()
{
return ZKFPM_DBClear(m_hDBCache);
}
int FingerprintCacheDB::addItem(unsigned int fingerID, QString base64String)
{
QByteArray blob = Base64ToBlob(base64String);
return addItem(fingerID, blob);
}
int FingerprintCacheDB::addItem(unsigned int fingerID, QByteArray regTemplate)
{
return ZKFPM_DBAdd(m_hDBCache, fingerID, reinterpret_cast<unsigned char *> (regTemplate.data()), regTemplate.size());
}
int FingerprintCacheDB::deleteItem(unsigned int fingerID)
{
return ZKFPM_DBDel(m_hDBCache, fingerID);
}
int FingerprintCacheDB::count() const
{
if(m_hDBCache)
{
int c;
ZKFPM_DBCount(m_hDBCache, reinterpret_cast<unsigned int*> (&c));
return c;
}
return 0;
}
int FingerprintCacheDB::verifyFinger(QByteArray regTemplate, QByteArray verTemplate)
{
if(m_hDBCache)
{
int score = ZKFPM_DBMatch(m_hDBCache,
reinterpret_cast<unsigned char *> (regTemplate.data()), regTemplate.size(),
reinterpret_cast<unsigned char *> (verTemplate.data()), verTemplate.size());
return score;
}
return -1;
}
int FingerprintCacheDB::mergeFinger(QByteArray in1, QByteArray in2, QByteArray in3, QByteArray &outTemplate)
{
if(m_hDBCache)
{
unsigned int cbRegTemp = MAX_TEMPLATE_SIZE;
outTemplate.resize(MAX_TEMPLATE_SIZE);
outTemplate.fill(0);
int ret = ZKFPM_DBMerge(m_hDBCache,
reinterpret_cast<unsigned char *> (in1.data()),
reinterpret_cast<unsigned char *> (in2.data()),
reinterpret_cast<unsigned char *> (in3.data()),
reinterpret_cast<unsigned char *> (outTemplate.data()), &cbRegTemp);
outTemplate.resize(cbRegTemp);
return ret;
}
return -1;
}
int FingerprintCacheDB::identify(QString base64String, unsigned int &fingerID, unsigned int &score)
{
QByteArray fpTemp = Base64ToBlob(base64String);
return identify(fpTemp, fingerID, score);
}
int FingerprintCacheDB::identify(QByteArray inTemplate, unsigned int &fingerID, unsigned int &score)
{
return ZKFPM_DBIdentify(m_hDBCache,
reinterpret_cast<unsigned char *> (inTemplate.data()), inTemplate.size(),
&fingerID, &score);
}
}
FingerprintEnroll
FingerprintEnroll 用于将同一手指的三次采集到的指纹模板合成一个指纹模板。样合成后的指纹模板包含指纹信息更全面,判别准确率更高。这个过程的核心是个状态机,中间要判断每次获得的指纹是否合适,并剔除不合理的指纹。
下面是头文件;
#ifndef FINGERPRINTENROLL_H
#define FINGERPRINTENROLL_H
/**
*
*/
#include <QObject>
#include <QByteArray>
#include <QImage>
namespace ZKFP
{
class FingerprintReader;
class FingerprintCacheDB;
/**
* @brief The FingerprintEnroll class 辅助类,用于将同一手指的三次采集到的指纹模板合成一个指纹模板
* 这样合成后的指纹模板包含指纹信息更全面,判别准确率更高。
* 其实这个过程不应该叫 enroll, enroll 应该包括将结果注册到数据库中。
* 但是没想好更好的词,所以暂时就叫这个词吧。
*/
class FingerprintEnroll : public QObject
{
Q_OBJECT
public:
explicit FingerprintEnroll(FingerprintCacheDB *cacheDB = nullptr, QObject *parent = nullptr);
void setFingerprintCacheDB(FingerprintCacheDB *cacheDB);
QByteArray regTemplate() const {return m_regTemplate;}
signals:
void enrollFinished(QByteArray regTemplate);
void enrollFailed();
void fingerprintRejected();
void fingerprintAccepted();
public slots:
void reset();
bool addFPTemplate(QByteArray fpTemplate);
bool addFingerprint(QImage fingerImage, QByteArray fpTemplate);
void beginEnroll(ZKFP::FingerprintReader * reader);
void cancelEnroll();
private:
FingerprintReader * m_reader;
FingerprintCacheDB *m_cacheDB;
QByteArray m_fpTemplate[3];
QByteArray m_regTemplate;
bool m_bRegister;
int m_enrollIdx;
};
}
#endif // FINGERPRINTENROLL_H
之后是 cpp 文件:
#include "FingerprintEnroll.h"
#include "FingerprintCacheDB.h"
#include "FingerprintReader.h"
#include <QDebug>
namespace ZKFP
{
static const int ENROLLCNT = 3;
FingerprintEnroll::FingerprintEnroll(FingerprintCacheDB *cacheDB, QObject *parent)
: QObject(parent),
m_cacheDB(0),
m_reader(0),
m_enrollIdx(0),
m_bRegister(false)
{
m_cacheDB = cacheDB;
}
void FingerprintEnroll::setFingerprintCacheDB(FingerprintCacheDB *cacheDB)
{
m_cacheDB = cacheDB;
}
void FingerprintEnroll::reset()
{
if(m_cacheDB && m_reader)
{
disconnect(m_reader, SIGNAL(onFingerprintReceived(QImage,QByteArray)),
this, SLOT(addFingerprint(QImage,QByteArray)));
}
m_enrollIdx = 0;
m_bRegister = false;
m_regTemplate.clear();
m_fpTemplate[0].clear();
m_fpTemplate[1].clear();
m_fpTemplate[2].clear();
}
void FingerprintEnroll::beginEnroll(FingerprintReader * reader)
{
reset();
m_reader = reader;
if(m_cacheDB && m_reader)
{
connect(m_reader, SIGNAL(onFingerprintReceived(QImage,QByteArray)),
this, SLOT(addFingerprint(QImage,QByteArray)));
}
m_reader->beginCapture();
}
void FingerprintEnroll::cancelEnroll()
{
reset();
m_reader->cancelCapture();
}
bool FingerprintEnroll::addFingerprint(QImage fingerImage, QByteArray fpTemplate)
{
Q_UNUSED(fingerImage);
return addFPTemplate(fpTemplate);
}
bool FingerprintEnroll::addFPTemplate(QByteArray fpTemplate)
{
qDebug() << "m_enrollIdx = " << m_enrollIdx;
if(m_cacheDB == 0)
{
return false;
}
if (m_enrollIdx >= ENROLLCNT)
{
//reset();
return false;
}
if (m_enrollIdx > 0)
{
int score = m_cacheDB->verifyFinger(m_fpTemplate[m_enrollIdx - 1], fpTemplate);
qDebug() << "score = " << score;
if (score <= 0)
{
//m_enrollIdx = 0;
//m_bRegister = false;
emit fingerprintRejected();
qDebug() << "fingerprintRejected()";
return false;
}
}
m_fpTemplate[m_enrollIdx] = fpTemplate;
if (++m_enrollIdx >= ENROLLCNT)
{
int ret = m_cacheDB->mergeFinger(m_fpTemplate[0], m_fpTemplate[1], m_fpTemplate[2], m_regTemplate);
//m_enrollIdx = 0;
m_bRegister = FALSE;
if (0 == ret)
{
emit enrollFinished(m_regTemplate);
disconnect(m_reader, SIGNAL(onFingerprintReceived(QImage,QByteArray)),
this, SLOT(addFingerprint(QImage,QByteArray)));
m_reader->cancelCapture();
qDebug() << "enrollFinished()";
return true;
}
else
{
emit enrollFailed();
disconnect(m_reader, SIGNAL(onFingerprintReceived(QImage,QByteArray)),
this, SLOT(addFingerprint(QImage,QByteArray)));
qDebug() << "enrollFailed()";
return false;
}
if(m_reader)
{
disconnect(m_reader, SIGNAL(onFingerprintReceived(QImage,QByteArray)),
this, SLOT(addFingerprint(QImage,QByteArray)));
}
}
else
{
emit fingerprintAccepted();
qDebug() << "fingerprintAccepted()";
return true;
}
return false;
}
}
FPCacheDBLoader
FPCacheDBLoader 是个数据库代码。用于将指纹存储到数据库,或者从数据库中读出。因为功能比较见到那,所以使用的是 SQLite。这个数据库功能比较少,但是对这个项目来说也足够了。这个代码只是个示例,不同的项目中肯定还要根据具体的需求来改写。
下面是头文件:
#ifndef FPCACHEDBLOADER_H
#define FPCACHEDBLOADER_H
#include <QObject>
#include <QList>
#include <QString>
#include <QMap>
#include <QSql>
#include <QSqlDatabase>
namespace ZKFP
{
class FingerprintCacheDB;
/**
* @brief The FPCacheDBLoader class 辅助类,用于加载指纹数据库。
*/
class FPCacheDBLoader : public QObject
{
Q_OBJECT
public:
explicit FPCacheDBLoader(QObject *parent = nullptr);
/**
* @brief loadFromFile 从文件中加载指纹数据库,并将指纹数据送到 db
* @param fileName 保存指纹数据的文件
* @param db
* @return true 表示成功, false 表示失败
*/
bool loadFromFile(QString fileName, FingerprintCacheDB *fpDB);
void initFPCacheDB(FingerprintCacheDB *fpDB);
QList<int> userIDs();
QString userName(int userID);
QMap<int, QString> userNames();
bool userInfo(int userID, QString &name, QString &password, QByteArray &fpTemplate);
bool addUser(int userID, QString name, QString password, QByteArray fpTemplate);
bool deleteUser(int userID);
signals:
public slots:
private:
QSqlDatabase m_database;
};
}
#endif // FPCACHEDBLOADER_H
之后是 cpp 文件:
#include "FPCacheDBLoader.h"
#include <QSqlQuery>
#include <QDebug>
#include <QDir>
#include <QStandardPaths>
#include "ZKFingerReader/FingerprintCacheDB.h"
namespace ZKFP
{
FPCacheDBLoader::FPCacheDBLoader(QObject *parent) : QObject(parent)
{
m_database = QSqlDatabase::addDatabase("QSQLITE");
QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
QDir dir;
dir.mkdir(path);
path += "/fingerprintDB.db";
qDebug() << path;
m_database.setDatabaseName(path);
m_database.open();
QSqlQuery query(m_database);
query.exec("CREATE TABLE IF NOT EXISTS [fingerprint]("
"user_id INTEGER NOT NULL UNIQUE,"
"name TEXT,"
"password TEXT,"
"fingerprint BLOB);");
}
QMap<int, QString> FPCacheDBLoader::userNames()
{
QSqlQuery query(m_database);
query.setForwardOnly(true);
query.prepare("SELECT user_id, name FROM fingerprint");
query.exec();
QMap<int, QString> names;
while( query.next( ) )
{
int id = query.value(0).toInt();
QString name = query.value(1).toString();
names[id] = name;
}
return names;
}
QList<int> FPCacheDBLoader::userIDs()
{
QSqlQuery query(m_database);
query.setForwardOnly(true);
query.prepare("SELECT user_id FROM fingerprint");
query.exec();
QList<int> IDs;
while( query.next( ) )
{
int id = query.value(0).toInt();
IDs << id;
}
return IDs;
}
bool FPCacheDBLoader::addUser(int userID, QString name, QString password, QByteArray fpTemplate)
{
QSqlQuery query(m_database);
query.prepare("INSERT into fingerprint(user_id, name, password, fingerprint) VALUES(:user_id, :name,:password, :fingerprint) ;");
query.bindValue(":user_id", userID);
query.bindValue(":name", name);
query.bindValue(":password", password);
query.bindValue(":fingerprint", fpTemplate);
return query.exec();
}
bool FPCacheDBLoader::userInfo(int userID, QString &name, QString &password, QByteArray &fpTemplate)
{
QSqlQuery query(m_database);
query.setForwardOnly(true);
query.prepare("SELECT * FROM fingerprint WHERE user_id = :id");
query.bindValue(":id", userID);
query.exec();
if( query.next( ) )
{
name = query.value(1).toString();
password = query.value(2).toString();
fpTemplate = query.value(3).toByteArray();
return true;
}
return false;
}
bool FPCacheDBLoader::deleteUser(int userID)
{
QSqlQuery query(m_database);
query.prepare("DELETE FROM fingerprint WHERE user_id = :id");
query.bindValue(":id", userID);
return query.exec();
}
QString FPCacheDBLoader::userName(int userID)
{
QSqlQuery query(m_database);
query.setForwardOnly(true);
query.prepare("SELECT name FROM fingerprint WHERE user_id = :id");
query.bindValue(":id", userID);
query.exec();
if( query.next( ) )
{
QString name = query.value(0).toString();
return name;
}
return QString();
}
void FPCacheDBLoader::initFPCacheDB(FingerprintCacheDB *fpDB)
{
fpDB->clearAll();
QSqlQuery query(m_database);
query.setForwardOnly(true);
query.exec("SELECT user_id, fingerprint FROM fingerprint");
while( query.next( ) )
{
int fid = query.value(0).toInt();
QByteArray fpTemplate = query.value(1).toByteArray();
fpDB->addItem(fid, fpTemplate);
qDebug() << "fid = " << fid;
}
}
bool FPCacheDBLoader::loadFromFile(QString fileName, ZKFP::FingerprintCacheDB *fpDB)
{
QSqlDatabase database = QSqlDatabase::addDatabase("QSQLITE");
database.setDatabaseName(fileName);
if(database.open())
{
qDebug() << "database open succ";
fpDB->clearAll();
QSqlQuery query(database);
query.setForwardOnly(true);
query.exec("SELECT user_id, fingerprint FROM fingerprint");
while( query.next( ) )
{
int fid = query.value(0).toInt();
QByteArray fpTemplate = query.value(1).toByteArray();
fpDB->addItem(fid, fpTemplate);
}
return true;
}
return false;
}
}