构建项目工程
导入项目依赖
<properties> <java.version>1.8</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <spring-boot.version>2.3.7.RELEASE</spring-boot.version> <druid-spring-boot-starter.version>1.2.6</druid-spring-boot-starter.version> <mybatis-spring-boot.version>2.1.4</mybatis-spring-boot.version> <pagehelper.boot.version>1.3.0</pagehelper.boot.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 人脸识别 --> <dependency> <groupId>com.arcsoft.face</groupId> <artifactId>arcsoft-sdk-face</artifactId> <scope>system</scope> <systemPath>${project.basedir}/lib/arcsoft-sdk-face-3.0.0.0.jar</systemPath> <version>3.0.0.0</version> </dependency> <!-- pool 对象池 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <!-- guava --> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>27.0.1-jre</version> </dependency> <!-- Mysql驱动包 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- mybatis-plus 增强CRUD --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.2</version> </dependency> <!-- pagehelper 分页插件 内置mybatis 依赖--> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>${pagehelper.boot.version}</version> </dependency> <!-- 阿里数据源 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>${druid-spring-boot-starter.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <!-- Spring框架基本的核心工具 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> </dependency> <!-- JSON工具类 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.0</version> </dependency> <!-- SpringWeb模块 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> </dependency> <!-- servlet包 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> </dependency> <!-- aop --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.3.7.RELEASE</version> <configuration> <mainClass>top.yangbuyi.YangbuyiFaceDemoApplication</mainClass> <!-- 文件要配置includeSystemScope属性,否则可能会导致arcsoft-sdk-face-3.0.0.0.jar获取不到 --> <includeSystemScope>true</includeSystemScope> <fork>true</fork> </configuration> <executions> <execution> <id>repackage</id> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
使用代码生成器生成CRUD
版本请对应图片当中的jebat全家桶群里获取3秒破解使用
生成完毕后在根目录创建lib目录将下载下来的人脸识别依赖导入->右击添加到库
application.yml 修改配置文件
# 开发环境配置 server: # 服务器的HTTP端口,默认为8080 port: 8080 servlet: # 应用的访问路径 context-path: / tomcat: # tomcat的URI编码 uri-encoding: UTF-8 # tomcat最大线程数,默认为200 max-threads: 800 # Tomcat启动初始化的线程数,默认值25 min-spare-threads: 30 # Spring配置 spring: # 同时执行其它配置文件 profiles: active: druid mvc: # 把前端的接收到的时间格式 格式化为 yyyy-MM-dd HH:mm:ss date-format: yyyy-MM-dd HH:mm:ss jackson: # 把后台的时间格式 格式化为 yyyy-MM-dd HH:mm:ss date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 # 服务模块 devtools: restart: # 热部署开关 enabled: true # MyBatis配置 mybatis-plus: # 搜索指定包别名 typeAliasesPackage: top.yangbuyi.domain # 配置mapper的扫描,找到所有的mapper.xml映射文件 mapperLocations: classpath*:mapper/*Mapper.xml # 加载全局的配置文件 configLocation: classpath:mybatis/mybatis-config.xml # PageHelper分页插件 pagehelper: helperDialect: mysql reasonable: true supportMethodsArguments: true params: count=countSql # 人脸识别配置 # WIND64 config: sdk-lib-path: M:\yangbuyiya-RBAC\libs\WIN64 app-id: 4QKtmacvsKqaCsoXyyujcs21JTAr79pTczPdZpuaEjhH sdk-key: EgBjrmidnqstaL46msfHukeKanYXCujzeHokf2qcC3br thread-pool-size: 5
application-druid.yml 数据源配置
# 数据源配置 spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.cj.jdbc.Driver druid: url: jdbc:mysql://127.0.0.1:3308/face?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: root password: 123456 # 初始连接数 initialSize: 5 # 最小连接池数量 minIdle: 10 # 最大连接池数量 maxActive: 20 # 配置获取连接等待超时的时间 maxWait: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 timeBetweenEvictionRunsMillis: 60000 # 配置一个连接在池中最小生存的时间,单位是毫秒 minEvictableIdleTimeMillis: 300000 # 配置一个连接在池中最大生存的时间,单位是毫秒 maxEvictableIdleTimeMillis: 900000 # 配置检测连接是否有效 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false webStatFilter: enabled: true statViewServlet: enabled: true # 设置白名单,不填则允许所有访问 allow: url-pattern: /yangbuyi/druid/* filter: stat: enabled: true # 慢SQL记录 log-slow-sql: true slow-sql-millis: 1000 merge-sql: true wall: config: multi-statement-allow: true
resources 下创建mybatis文件夹创建mybatis-config.xml文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <setting name="cacheEnabled" value="true"/> <!-- 全局映射器启用缓存 --> <setting name="useGeneratedKeys" value="true"/> <!-- 允许 JDBC 支持自动生成主键 --> <setting name="defaultExecutorType" value="REUSE"/> <!-- 配置默认的执行器 --> <setting name="logImpl" value="SLF4J"/> <!-- 指定 MyBatis 所用日志的具体实现 --> <!-- <setting name="mapUnderscoreToCamelCase" value="true"/> 驼峰式命名 --> </settings> </configuration>
项目基础文件配置
创建config文件
创建ApplicationConfig全局配置类
/** * 程序注解配置 * * @author yangbuyi */ @Configuration // 表示通过aop框架暴露该代理对象,AopContext能够访问 @EnableAspectJAutoProxy(exposeProxy = true) // 指定要扫描的Mapper类的包的路径 @MapperScan("top.yangbuyi.mapper") public class ApplicationConfig { /** * 时区配置 */ @Bean public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization () { return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault()); } /** * 处理Long类型精度丢失 * * @return */ @Bean("jackson2ObjectMapperBuilderCustomizer") public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer () { return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.serializerByType(Long.class, ToStringSerializer.instance) .serializerByType(Long.TYPE, ToStringSerializer.instance); } }
创建全局MybatisPlusConfig配置
/** * @program: yangbuyi-rbac * @ClassName: MybatisPlusConfig * @create: 2022-04-25 15:48 * @author: yby6.com * @since: JDK1.8 * @MybatisPlusConfig: $ **/ import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.annotation.EnableTransactionManagement; @EnableTransactionManagement(proxyTargetClass = true) @Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor () { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 分页插件 interceptor.addInnerInterceptor(paginationInnerInterceptor()); // 乐观锁插件 interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor()); // 阻断插件 interceptor.addInnerInterceptor(blockAttackInnerInterceptor()); return interceptor; } /** * 分页插件,自动识别数据库类型 https://baomidou.com/guide/interceptor-pagination.html */ public PaginationInnerInterceptor paginationInnerInterceptor () { PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(); // 设置数据库类型为mysql paginationInnerInterceptor.setDbType(DbType.MYSQL); // 设置最大单页限制数量,默认 500 条,-1 不受限制 paginationInnerInterceptor.setMaxLimit(-1L); return paginationInnerInterceptor; } /** * 乐观锁插件 https://baomidou.com/guide/interceptor-optimistic-locker.html */ public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor () { return new OptimisticLockerInnerInterceptor(); } /** * 如果是对全表的删除或更新操作,就会终止该操作 https://baomidou.com/guide/interceptor-block-attack.html */ public BlockAttackInnerInterceptor blockAttackInnerInterceptor () { return new BlockAttackInnerInterceptor(); } }
创建dto文件夹
创建人脸返回实体 FaceSearchResDto
/** * @author yby6.com */ @Data public class FaceSearchResDto { /** * 唯一人脸Id */ private String faceId; /** * 人脸名称 */ private String name; private Integer similarValue; /** * 年龄 */ private Integer age; /** * 性别 */ private String gender; /** * 图片 */ private String image; }
创建人脸映射 FaceUserInfo
/** * @author yby6.com */ @Data public class FaceUserInfo { private int id; private int groupId; private String faceId; private String name; private Integer similarValue; private byte[] faceFeature; }
创建年龄映射
/** * @author yby6.com */ public class ProcessInfo { private Integer age; private Integer gender; public Integer getAge () { return age; } public void setAge (Integer age) { this.age = age; } public Integer getGender () { return gender; } public void setGender (Integer gender) { this.gender = gender; } }
创建enums文件夹
创建ErrorCodeEnum枚举类
/** * @author yby6.com */ public enum ErrorCodeEnum { MOK(0, "成功"), UNKNOWN(1, "未知错误"), INVALID_PARAM(2, "无效参数"), UNSUPPORTED(3, "引擎不支持"), NO_MEMORY(4, "内存不足"), BAD_STATE(5, "状态错误"), USER_CANCEL(6, "用户取消相关操作"), EXPIRED(7, "操作时间过期"), USER_PAUSE(8, "用户暂停操作"), BUFFER_OVERFLOW(9, "缓冲上溢"), BUFFER_UNDERFLOW(10, "缓冲下溢"), NO_DISKSPACE(11, "存贮空间不足"), COMPONENT_NOT_EXIST(12, "组件不存在"), GLOBAL_DATA_NOT_EXIST(13, "全局数据不存在"), NO_FACE_DETECTED(14, "未检出到人脸"), FACE_DOES_NOT_MATCH(15, "人脸不匹配"), INVALID_APP_ID(28673, "无效的AppId"), INVALID_SDK_ID(28674, "无效的SdkKey"), INVALID_ID_PAIR(28675, "AppId和SdkKey不匹配"), MISMATCH_ID_AND_SDK(28676, "SdkKey 和使用的SDK 不匹配"), SYSTEM_VERSION_UNSUPPORTED(28677, "系统版本不被当前SDK所支持"), LICENCE_EXPIRED(28678, "SDK有效期过期,需要重新下载更新"), APS_ENGINE_HANDLE(69633, "引擎句柄非法"), APS_MEMMGR_HANDLE(69634, "内存句柄非法"), APS_DEVICEID_INVALID(69635, " Device ID 非法"), APS_DEVICEID_UNSUPPORTED(69636, "Device ID 不支持"), APS_MODEL_HANDLE(69637, "模板数据指针非法"), APS_MODEL_SIZE(69638, "模板数据长度非法"), APS_IMAGE_HANDLE(69639, "图像结构体指针非法"), APS_IMAGE_FORMAT_UNSUPPORTED(69640, "图像格式不支持"), APS_IMAGE_PARAM(69641, "图像参数非法"), APS_IMAGE_SIZE(69642, "图像尺寸大小超过支持范围"), APS_DEVICE_AVX2_UNSUPPORTED(69643, "处理器不支持AVX2指令"), FR_INVALID_MEMORY_INFO(73729, "无效的输入内存"), FR_INVALID_IMAGE_INFO(73730, "无效的输入图像参数"), FR_INVALID_FACE_INFO(73731, "无效的脸部信息"), FR_NO_GPU_AVAILABLE(73732, "当前设备无GPU可用"), FR_MISMATCHED_FEATURE_LEVEL(73733, "待比较的两个人脸特征的版本不一致"), FACEFEATURE_UNKNOWN(81921, "人脸特征检测错误未知"), FACEFEATURE_MEMORY(81922, "人脸特征检测内存错误"), FACEFEATURE_INVALID_FORMAT(81923, "人脸特征检测格式错误"), FACEFEATURE_INVALID_PARAM(81924, "人脸特征检测参数错误"), FACEFEATURE_LOW_CONFIDENCE_LEVEL(81925, "人脸特征检测结果置信度低"), ASF_EX_BASE_FEATURE_UNSUPPORTED_ON_INIT(86017, "Engine不支持的检测属性"), ASF_EX_BASE_FEATURE_UNINITED(86018, "需要检测的属性未初始化"), ASF_EX_BASE_FEATURE_UNPROCESSED(86019, "待获取的属性未在process中处理过"), ASF_EX_BASE_FEATURE_UNSUPPORTED_ON_PROCESS(86020, "PROCESS不支持的检测属性,例如FR,有自己独立的处理函数"), ASF_EX_BASE_INVALID_IMAGE_INFO(86021, "无效的输入图像"), ASF_EX_BASE_INVALID_FACE_INFO(86022, "无效的脸部信息"), ASF_BASE_ACTIVATION_FAIL(90113, "人脸比对SDK激活失败,请打开读写权限"), ASF_BASE_ALREADY_ACTIVATED(90114, "人脸比对SDK已激活"), ASF_BASE_NOT_ACTIVATED(90115, "人脸比对SDK未激活"), ASF_BASE_SCALE_NOT_SUPPORT(90116, "detectFaceScaleVal 不支持"), ASF_BASE_VERION_MISMATCH(90117, "SDK版本不匹配"), ASF_BASE_DEVICE_MISMATCH(90118, "设备不匹配"), ASF_BASE_UNIQUE_IDENTIFIER_MISMATCH(90119, "唯一标识不匹配"), ASF_BASE_PARAM_NULL(90120, "参数为空"), ASF_BASE_SDK_EXPIRED(90121, "SDK已过期"), ASF_BASE_VERSION_NOT_SUPPORT(90122, "版本不支持"), ASF_BASE_SIGN_ERROR(90123, "签名错误"), ASF_BASE_DATABASE_ERROR(90124, "数据库插入错误"), ASF_BASE_UNIQUE_CHECKOUT_FAIL(90125, "唯一标识符校验失败"), ASF_BASE_COLOR_SPACE_NOT_SUPPORT(90126, "输入的颜色空间不支持"), ASF_BASE_IMAGE_WIDTH_NOT_SUPPORT(90127, "输入图像的byte数据长度不正确"), ASF_NETWORK_BASE_COULDNT_RESOLVE_HOST(94209, "无法解析主机地址"), ASF_NETWORK_BASE_COULDNT_CONNECT_SERVER(94210, "无法连接服务器"), ASF_NETWORK_BASE_CONNECT_TIMEOUT(94211, "网络连接超时"), ASF_NETWORK_BASE_UNKNOWN_ERROR(94212, "未知错误"); private Integer code; private String description; ErrorCodeEnum (Integer code, String description) { this.code = code; this.description = description; } public Integer getCode () { return code; } public void setCode (Integer code) { this.code = code; } public String getDescription () { return description; } public void setDescription (String description) { this.description = description; } public static ErrorCodeEnum getDescriptionByCode (Integer code) { for (ErrorCodeEnum errorCodeEnum : ErrorCodeEnum.values()) { if (code.equals(errorCodeEnum.getCode())) { return errorCodeEnum; } } return ErrorCodeEnum.UNKNOWN; } }
创建utils文件夹
创建Base64DecodeMultipartFile
package top.yangbuyi.utils; import org.springframework.web.multipart.MultipartFile; import java.io.*; /** * @author yby6.com * @program: yangbuyi-rbac * @ClassName: Base64DecodeMultipartFile * @create: 2022-04-25 16:25 * @since: JDK1.8 * @Base64DecodeMultipartFile: $ **/ public class Base64DecodeMultipartFile implements MultipartFile { private final byte[] imgContent; private final String header; public Base64DecodeMultipartFile (byte[] imgContent, String header) { this.imgContent = imgContent; this.header = header.split(";")[0]; } @Override public String getName () { return System.currentTimeMillis() + Math.random() + "." + header.split("/")[1]; } @Override public String getOriginalFilename () { return System.currentTimeMillis() + (int) Math.random() * 10000 + "." + header.split("/")[1]; } @Override public String getContentType () { return header.split(":")[1]; } @Override public boolean isEmpty () { return imgContent == null || imgContent.length == 0; } @Override public long getSize () { return imgContent.length; } @Override public byte[] getBytes () throws IOException { return imgContent; } @Override public InputStream getInputStream () throws IOException { return new ByteArrayInputStream(imgContent); } @Override public void transferTo (File dest) throws IOException, IllegalStateException { new FileOutputStream(dest).write(imgContent); } }
创建ImageUtils
package top.yangbuyi.utils; import org.apache.tomcat.util.codec.binary.Base64; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.multipart.MultipartFile; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; import java.util.Arrays; /** * 图片处理工具类 * * @author yby6.com */ public class ImageUtils { private static final Logger log = LoggerFactory.getLogger(ImageUtils.class); public static MultipartFile base64ToMultipartFile (String base64) { //base64编码后的图片有头信息所以要分离出来 [0]data:image/png;base64, 图片内容为索引[1] String[] baseStrs = base64.split(","); //取索引为1的元素进行处理 byte[] b = Base64.decodeBase64(baseStrs[1]); for (int i = 0; i < b.length; ++i) { if (b[i] < 0) { b[i] += 256; } } //处理过后的数据通过Base64DecodeMultipartFile转换为MultipartFile对象 return new Base64DecodeMultipartFile(b, baseStrs[0]); } }
项目基本需要的配置与工具已经创建完毕接下来我们开始人脸识别业务编写
实现BasePooledObjectFactory自定义引擎工厂
创建factory文件夹->创建FaceEngineFactory类
import com.arcsoft.face.EngineConfiguration; import com.arcsoft.face.FaceEngine; import com.arcsoft.face.enums.DetectMode; import com.arcsoft.face.enums.DetectOrient; import lombok.extern.slf4j.Slf4j; import org.apache.commons.pool2.BasePooledObjectFactory; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.impl.DefaultPooledObject; /** * 引擎工厂 * @author yby6.com */ @Slf4j public class FaceEngineFactory extends BasePooledObjectFactory<FaceEngine> { private final String appId; private final String sdkKey; private final String sdkLibPath; private final EngineConfiguration engineConfiguration; private final Integer detectFaceMaxNum = 10; private final Integer detectFaceScaleVal = 16; private final DetectMode detectMode = DetectMode.ASF_DETECT_MODE_IMAGE; private final DetectMode detectVideo = DetectMode.ASF_DETECT_MODE_VIDEO; private final DetectOrient detectFaceOrientPriority = DetectOrient.ASF_OP_0_ONLY; public FaceEngineFactory (String sdkLibPath, String appId, String sdkKey, EngineConfiguration engineConfiguration) { this.sdkLibPath = sdkLibPath; this.appId = appId; this.sdkKey = sdkKey; this.engineConfiguration = engineConfiguration; } @Override public FaceEngine create () throws Exception { FaceEngine faceEngine = new FaceEngine(sdkLibPath); // 用于在线激活SDK--- int activeCode = faceEngine.activeOnline(appId, sdkKey); log.info("在线激活SDK完毕!,{}", activeCode); int initCode = faceEngine.init(engineConfiguration); log.info("初始化功能引擎完毕!,{}", initCode); return faceEngine; } @Override public PooledObject<FaceEngine> wrap (FaceEngine faceEngine) { return new DefaultPooledObject<>(faceEngine); } @Override public void destroyObject (PooledObject<FaceEngine> p) throws Exception { FaceEngine faceEngine = p.getObject(); int unInitCode = faceEngine.unInit(); super.destroyObject(p); log.info("销毁对象完毕! faceEngineUnInitCode: {}", unInitCode); } }