Face recognition (Java+ Face++ implementation)

Face recognition ( Java + Face++ implementation)

在这里插入图片描述

I. Overview

The core technology of Face++ is face recognition technology based on deep learning, and its algorithm is in the leading position in terms of accuracy and speed. The company's products and services include face recognition SDK, face recognition API, face comparison service, face detection service, living body detection service, etc. These products and services are widely used in finance, public security, retail, logistics and other fields. Moreover, Face++ provides a free API interface for developers to use, so we can borrow this open API at a very small cost to complete our functional requirements design for the project!

Official website address https://www.faceplusplus.com.cn/

API document address https://console.faceplusplus.com.cn/documents/4887586

Free source address https://download.csdn.net/download/weixin_53742691/87855700

image-20230601164002717

image-20230601160904624

2. Core term concepts

human face

Face (Face) in face recognition technology specifically refers to the face found in the image. When a face is detected on a picture, the detected face will be recorded, including the position of the face in the picture. It is represented by a system identifier face_token. Note: If multiple face detections are performed on the same picture, different face_tokens will be obtained for the same face.

Face database

The face library (FaceSet) is used to store the storage objects of detected faces. The face_token in a FaceSet is not repeated.

Face feature identification (face_token)

Face_token 是系统为人脸分配的唯一标识。当对一张图片进行人脸检测后,检测到的人脸以及人脸在图片中的位置会用一个用一个人脸特征标识 face_token 来表示。在进行人脸比对或人脸关键点检测时必须指定 face_token。
请注意:针对同一张图片进行多次人脸检测,同一个人脸会得到不同的人脸特征标识 (face_token) 。

人脸比对/人脸搜索

计算机检测到图片中一个人脸之后,通过人脸判断人身份的过程被称为人脸比对/人脸搜索。

人脸比对指采集新的人脸,与一个已知身份用户的人脸进行比对,判断新的人脸是否属于该已知身份用户。人脸比对需要调用人脸比对 API。

人脸搜索是指采集用户新的人脸,在多个已知身份用户的人脸集合中进行搜索,找出新的人脸属于哪一个已知身份用户。人脸搜索需要调用人脸搜索API。

API Key

API Key 是使用公有云 API 和联网授权 SDK 服务的凭证。注册账号后,需要先创建一个 API Key,才能进行接口调用服务。
请注意:API Key 分为试用(免费)与正式(服务)两种类型,试用 API Key 在创建数量、使用的服务类型和并发 (QPS) 保障上均有限制。

API Secret

API Secret 是在创建 API Key 时随机生成的一串密钥,需要和 API Key 搭配,获取使用 API 的权限,请您妥善保管记录。

Bundle ID(包名)

Bundle ID 是 APP 的唯一标识,如果需要在 APP 内集成 SDK,首先需要绑定Bundle ID(包名)。
请注意:如果仅使用 API 服务,不需要创建 Bundle ID。

并发 (QPS)

Concurrency (QPS) refers to the number of API requests that can be initiated per second. Calling each API under the same function module will calculate QPS uniformly. For example, the concurrent face recognition (QPS) is 10, and face recognition includes face detection API, face comparison API, face search API, face library management API group, face information acquisition API and custom face information API, you can initiate 10 API call requests per second, and there is no limit to which API is called. If more than 10 requests are made, a 403 concurrency exceeded error code (CONCURRENCY_LIMIT_EXCEEDED) will be returned.

3. Interface call description

describe

Compare two faces to determine whether they are the same person, and return the confidence of the comparison result and the threshold under different misrecognition rates.

Support to pass in pictures or face_token for comparison. When using a picture, the face with the largest face size detected in the picture will be automatically selected.

picture requirements

Image format: JPG (JPEG), PNG
Image pixel size: minimum 48 48 pixels, maximum 4096 4096 pixels
Image file size: 2MB
Minimum face pixel size: The face frame that the system can detect is a square with the minimum square side length The value is 150 pixels.

call URL

https://api-cn.faceplusplus.com/facepp/v3/compare

call method

POST

Request body format

multipart/form-data

permissions

All API Keys can call this API. Among them, the face_rectangle1 and face_rectangle2 parameters can only be used by the official API Key, and cannot be used by the trial API Key.

request parameters

Is it required? parameter name type Parameter Description
required api_key String API Key for calling this API
required api_secret String API Secret for calling this API
Mandatory (choose one from four) face_token1 String The first face identification face_token, this parameter is used first
image_url1 String URL of the first image
image_file1 File The first picture, a binary file, needs to be uploaded by post multipart/form-data.
image_base64_1 String If the image_url1, image_file1, and image_base64_1 parameters are passed in at the same time for base64-encoded binary image data, image_file1 is used first in this API, and image_url1 is the lowest.
Mandatory (choose one from four) face_token2 String The second face identification face_token, this parameter is preferred
image_url2 String The URL of the second image
image_file2 File The second image, a binary file, needs to be uploaded by post multipart/form-data.
image_base64_2 String If image_url2, image_file2 and image_base64_2 parameters are passed in base64-encoded binary image data at the same time, the order of use in this API is image_file2 first and image_url2 lowest.
Optional (only official API Key can be used) face_rectangle1 String Whether to specify the position of the face frame for detection when the incoming image is used for face detection. If the value of this parameter is empty, or if this parameter is not passed in, this function will not be used. This API automatically detects all faces in all regions of the image. Use this function if you use the official API Key to pass in a value that meets the format requirements for this parameter. A character string needs to be passed in to represent the position of the face frame, and the system will perform face detection on the image in the frame according to this coordinate, as well as follow-up operations such as face key points and face attributes. The position of the face rectangle returned by the system will be exactly the same as the face_rectangle passed in. For areas outside this face frame, the system will not perform face detection, nor will it return any other face information. Parameter specification: Four positive integers, separated by commas, respectively represent the ordinate of the upper left corner of the face frame (top), the abscissa of the upper left corner (left), the width of the face frame (width), and the height of the face frame (height). For example: 70,80,100,100 Note: This parameter takes effect only when any one of the three parameters image_url1, image_file1 and image_base64_1 is passed in.
Optional (only official API Key can be used) face_rectangle2 String Whether to specify the position of the face frame for detection when the incoming image is used for face detection. If the value of this parameter is empty, or if this parameter is not passed in, this function will not be used. This API automatically detects all faces in all regions of the image. Use this function if you use the official API Key to pass in a value that meets the format requirements for this parameter. A character string needs to be passed in to represent the position of the face frame, and the system will perform face detection on the image in the frame according to this coordinate, as well as follow-up operations such as face key points and face attributes. The position of the face rectangle returned by the system will be exactly the same as the face_rectangle passed in. For areas outside this face frame, the system will not perform face detection, nor will it return any other face information. Parameter specification: Four positive integers, separated by commas, respectively represent the ordinate of the upper left corner of the face frame (top), the abscissa of the upper left corner (left), the width of the face frame (width), and the height of the face frame (height). For example: 70,80,100,100 Note: This parameter will take effect only after passing in any one of the three parameters image_url2, image_file2 and image_base64_2.

Return value description

field type illustrate
request_id String 用于区分每一次请求的唯一的字符串。
confidence Float 比对结果置信度,范围 [0,100],小数点后3位有效数字,数字越大表示两个人脸越可能是同一个人。注:如果传入图片但图片中未检测到人脸,则无法进行比对,本字段不返回。
thresholds Object 一组用于参考的置信度阈值,包含以下三个字段。每个字段的值为一个 [0,100] 的浮点数,小数点后 3 位有效数字。1e-3:误识率为千分之一的置信度阈值;1e-4:误识率为万分之一的置信度阈值;1e-5:误识率为十万分之一的置信度阈值;如果置信值低于“千分之一”阈值则不建议认为是同一个人;如果置信值超过“十万分之一”阈值,则是同一个人的几率非常高。请注意:阈值不是静态的,每次比对返回的阈值不保证相同,所以没有持久化保存阈值的必要,更不要将当前调用返回的 confidence 与之前调用返回的阈值比较。注:如果传入图片但图片中未检测到人脸,则无法进行比对,本字段不返回。
image_id1 String 通过 image_url1、image_file1 或 image_base64_1 传入的图片在系统中的标识。注:如果未传入图片,本字段不返回。
image_id2 String 通过 image_url2、image_file2 或 image_base64_2 传入的图片在系统中的标识。注:如果未传入图片,本字段不返回。
faces1 Array 通过 image_url1、image_file1 或 image_base64_1 传入的图片中检测出的人脸数组,采用数组中的第一个人脸进行人脸比对。注:如果未传入图片,本字段不返回。如果没有检测出人脸则为空数组
faces2 Array 通过 image_url2、image_file2 或 image_base64_2 传入的图片中检测出的人脸数组,采用数组中的第一个人脸进行人脸比对。注:如果未传入图片,本字段不返回。如果没有检测出人脸则为空数组
time_used Int 整个请求所花费的时间,单位为毫秒。
error_message String 当请求失败时才会返回此字符串,具体返回内容见后续错误信息章节。否则此字段不存在。

faces1和faces2数组中单个元素的结构

字段 类型 说明
face_token String 人脸的标识
face_rectangle Object 人脸矩形框的位置,包括以下属性。每个属性的值都是整数:top:矩形框左上角像素点的纵坐标left:矩形框左上角像素点的横坐标width:矩形框的宽度height:矩形框的高度

返回值示例

成功请求返回值示例

{
    "time_used": 473, 
    "confidence": 96.46, 
    "thresholds": {
        "1e-3": 65.3, 
        "1e-5": 76.5, 
        "1e-4": 71.8
    }, 
    "request_id": "1469761507,07174361-027c-46e1-811f-ba0909760b18"
}

失败请求返回值示例

{
    "time_used": 5, 
    "error_message": "INVALID_FACE_TOKEN:c2fc0ad7c8da3af5a34b9c70ff764da0", 
    "request_id": "1469761051,ec285c20-8660-47d3-8b91-5dc2bffa0049"
}

当前 API 特有的 ERROR_MESSAGE

HTTP状态代码 错误信息 说明
400 INVALID_FACE_TOKEN: <face_token> 使用face_token作为参数时,所传的face_token不存在。
400 IMAGE_ERROR_UNSUPPORTED_FORMAT: 参数对应的图像无法正确解析,有可能不是一个图像文件、或有数据破损。
400 INVALID_IMAGE_SIZE: 参数对应的客户上传的图像像素尺寸太大或太小,图片要求请参照本API描述。对应图像太大的那个参数的名称
400 INVALID_IMAGE_URL: 无法从参数对应的image_url下载图片,图片URL错误或者无效。
400 IMAGE_FILE_TOO_LARGE: 参数对应的客户上传的图像文件太大。本 API 要求图片文件大小不超过 2 MB
403 INSUFFICIENT_PERMISSION: 试用 API Key 无法使用 对应的参数。请勿传入此参数。或者使用正式 API Key 调用。
412 IMAGE_DOWNLOAD_TIMEOUT: 下载图片超时。

通用的ERROR_MESSAGE

HTTP 状态代码 错误信息 说明
401 AUTHENTICATION_ERROR api_key和api_secret不匹配。
403 AUTHORIZATION_ERROR: api_key没有调用本API的权限,具体原因为:用户自己禁止该api_key调用、管理员禁止该api_key调用、由于账户余额不足禁止调用。目前的有:Denied by Client(用户自己禁止该api_key调用)Denied by Admin(管理员禁止该api_key调用)Insufficient Account Balance(由于账户余额不足禁止调用)
403 CONCURRENCY_LIMIT_EXCEEDED 并发数超过限制。注:这里的并发控制数超出限制,是指该API Key的QPS已经达到上限。如需要提高API Key的QPS配额请查看价格方案或者联系我们。
400 MISSING_ARGUMENTS: 缺少某个必选参数。
400 BAD_ARGUMENTS: 某个参数解析出错(比如必须是数字,但是输入的是非数字字符串; 或者长度过长,etc.)
400 COEXISTENCE_ARGUMENTS 同时传入了要求是二选一或多选一的参数。如有特殊说明则不返回此错误。
413 Request Entity Too Large 客户发送的请求大小超过了2MB限制。该错误的返回格式为纯文本,不是json格式。
404 API_NOT_FOUND 所调用的API不存在。
500 INTERNAL_ERROR 服务器内部错误,当此类错误发生时请再次请求,如果持续出现此类错误,请及时联系技术支持团队。

四. 后端代码设计

代码文件结构

在这里插入图片描述

引入相关依赖

	 <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--常用工具类 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</version>
        </dependency>

        <!-- JSON工具类 -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.13.2.2</version>
        </dependency>
        <!-- 阿里JSON解析器 -->
        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
            <version>2.0.26</version>
        </dependency>

        <!-- io常用工具类 -->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.11.0</version>
        </dependency>
        <!--    logback框架的依赖-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.30</version>
        </dependency>
        <!--        jwt鉴权相应依赖-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <version>0.11.2</version>
        </dependency>
    </dependencies>

通用常量信息

package com.xiaohui.faceapi.utils;

import io.jsonwebtoken.Claims;

/**
 * @Description 通用常量信息
 * @Author IT小辉同学
 * @Date 2023/06/01
 */
public class Constants
{
    
    
    /**
     * UTF-8 字符集
     */
    public static final String UTF8 = "UTF-8";

    /**
     * GBK 字符集
     */
    public static final String GBK = "GBK";

    /**
     * www主域
     */
    public static final String WWW = "www.";

    /**
     * http请求
     */
    public static final String HTTP = "http://";

    /**
     * https请求
     */
    public static final String HTTPS = "https://";

    /**
     * 通用成功标识
     */
    public static final String SUCCESS = "0";

    /**
     * 通用失败标识
     */
    public static final String FAIL = "1";

    /**
     * 登录成功
     */
    public static final String LOGIN_SUCCESS = "Success";

    /**
     * 注销
     */
    public static final String LOGOUT = "Logout";

    /**
     * 注册
     */
    public static final String REGISTER = "Register";

    /**
     * 登录失败
     */
    public static final String LOGIN_FAIL = "Error";
 
    /**
     * 验证码有效期(分钟)
     */
    public static final Integer CAPTCHA_EXPIRATION = 2;

    /**
     * 令牌
     */
    public static final String TOKEN = "token";

    /**
     * 令牌前缀
     */
    public static final String TOKEN_PREFIX = "Bearer ";

    /**
     * 令牌前缀
     */
    public static final String LOGIN_USER_KEY = "login_user_key";

    /**
     * 用户ID
     */
    public static final String JWT_USERID = "userid";

    /**
     * 用户名称
     */
    public static final String JWT_USERNAME = Claims.SUBJECT;

    /**
     * 用户头像
     */
    public static final String JWT_AVATAR = "avatar";

    /**
     * 创建时间
     */
    public static final String JWT_CREATED = "created";

    /**
     * 用户权限
     */
    public static final String JWT_AUTHORITIES = "authorities";

    /**
     * 资源映射路径 前缀
     */
    public static final String RESOURCE_PREFIX = "/profile";

    /**
     * RMI 远程方法调用
     */
    public static final String LOOKUP_RMI = "rmi:";

    /**
     * LDAP 远程方法调用
     */
    public static final String LOOKUP_LDAP = "ldap:";

    /**
     * LDAPS 远程方法调用
     */
    public static final String LOOKUP_LDAPS = "ldaps:";

    /**
     * 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加)
     */
    public static final String[] JOB_WHITELIST_STR = {
    
     "com.ruoyi" };

    /**
     * 定时任务违规的字符
     */
    public static final String[] JOB_ERROR_STR = {
    
     "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
            "org.springframework", "org.apache", "com.ruoyi.common.utils.file", "com.ruoyi.common.config" };
}

http请求状态

package com.xiaohui.faceapi.utils;

/**
 * @Description http请求状态
 * @Author IT小辉同学
 * @Date 2023/06/01
 */
public class HttpStatus
{
    
    
    /**
     * 操作成功
     */
    public static final int SUCCESS = 200;

    /**
     * 对象创建成功
     */
    public static final int CREATED = 201;

    /**
     * 请求已经被接受
     */
    public static final int ACCEPTED = 202;

    /**
     * 操作已经执行成功,但是没有返回数据
     */
    public static final int NO_CONTENT = 204;

    /**
     * 资源已被移除
     */
    public static final int MOVED_PERM = 301;

    /**
     * 重定向
     */
    public static final int SEE_OTHER = 303;

    /**
     * 资源没有被修改
     */
    public static final int NOT_MODIFIED = 304;

    /**
     * 参数列表错误(缺少,格式不匹配)
     */
    public static final int BAD_REQUEST = 400;

    /**
     * 未授权
     */
    public static final int UNAUTHORIZED = 401;

    /**
     * 访问受限,授权过期
     */
    public static final int FORBIDDEN = 403;

    /**
     * 资源,服务未找到
     */
    public static final int NOT_FOUND = 404;

    /**
     * 不允许的http方法
     */
    public static final int BAD_METHOD = 405;

    /**
     * 资源冲突,或者资源被锁
     */
    public static final int CONFLICT = 409;

    /**
     * 不支持的数据,媒体类型
     */
    public static final int UNSUPPORTED_TYPE = 415;

    /**
     * 系统内部错误
     */
    public static final int ERROR = 500;

    /**
     * 接口未实现
     */
    public static final int NOT_IMPLEMENTED = 501;

    /**
     * 系统警告消息
     */
    public static final int WARN = 601;
}

ajax请求结果封装

package com.xiaohui.faceapi.utils;


import java.util.HashMap;

/**
 * @Description ajax结果
 * @Author IT小辉同学
 * @Date 2023/06/01
 */
public class AjaxResult extends HashMap<String, Object>
{
    
    
    private static final long serialVersionUID = 1L;

    /** 状态码 */
    public static final String CODE_TAG = "code";

    /** 返回内容 */
    public static final String MSG_TAG = "msg";

    /** 数据对象 */
    public static final String DATA_TAG = "data";

    /**
     * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。
     */
    public AjaxResult()
    {
    
    
    }

    /**
     * 初始化一个新创建的 AjaxResult 对象
     *
     * @param code 状态码
     * @param msg 返回内容
     */
    public AjaxResult(int code, String msg)
    {
    
    
        super.put(CODE_TAG, code);
        super.put(MSG_TAG, msg);
    }

    /**
     * 初始化一个新创建的 AjaxResult 对象
     *
     * @param code 状态码
     * @param msg 返回内容
     * @param data 数据对象
     */
    public AjaxResult(int code, String msg, Object data)
    {
    
    
        super.put(CODE_TAG, code);
        super.put(MSG_TAG, msg);
        if (data!=null)
        {
    
    
            super.put(DATA_TAG, data);
        }
    }

    /**
     * 返回成功消息
     *
     * @return 成功消息
     */
    public static AjaxResult success()
    {
    
    
        return AjaxResult.success("操作成功");
    }

    /**
     * 返回成功数据
     *
     * @return 成功消息
     */
    public static AjaxResult success(Object data)
    {
    
    
        return AjaxResult.success("操作成功", data);
    }

    /**
     * 返回成功消息
     *
     * @param msg 返回内容
     * @return 成功消息
     */
    public static AjaxResult success(String msg)
    {
    
    
        return AjaxResult.success(msg, null);
    }

    /**
     * 返回成功消息
     *
     * @param msg 返回内容
     * @param data 数据对象
     * @return 成功消息
     */
    public static AjaxResult success(String msg, Object data)
    {
    
    
        return new AjaxResult(HttpStatus.SUCCESS, msg, data);
    }

    /**
     * 返回警告消息
     *
     * @param msg 返回内容
     * @return 警告消息
     */
    public static AjaxResult warn(String msg)
    {
    
    
        return AjaxResult.warn(msg, null);
    }

    /**
     * 返回警告消息
     *
     * @param msg 返回内容
     * @param data 数据对象
     * @return 警告消息
     */
    public static AjaxResult warn(String msg, Object data)
    {
    
    
        return new AjaxResult(HttpStatus.WARN, msg, data);
    }

    /**
     * 返回错误消息
     *
     * @return 错误消息
     */
    public static AjaxResult error()
    {
    
    
        return AjaxResult.error("操作失败");
    }

    /**
     * 返回错误消息
     *
     * @param msg 返回内容
     * @return 错误消息
     */
    public static AjaxResult error(String msg)
    {
    
    
        return AjaxResult.error(msg, null);
    }

    /**
     * 返回错误消息
     *
     * @param msg 返回内容
     * @param data 数据对象
     * @return 错误消息
     */
    public static AjaxResult error(String msg, Object data)
    {
    
    
        return new AjaxResult(HttpStatus.ERROR, msg, data);
    }

    /**
     * 返回错误消息
     *
     * @param code 状态码
     * @param msg 返回内容
     * @return 错误消息
     */
    public static AjaxResult error(int code, String msg)
    {
    
    
        return new AjaxResult(code, msg, null);
    }

    /**
     * 方便链式调用
     *
     * @param key 键
     * @param value 值
     * @return 数据对象
     */
    @Override
    public AjaxResult put(String key, Object value)
    {
    
    
        super.put(key, value);
        return this;
    }
}

通用http发送方法

package com.xiaohui.faceapi.utils;

import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import java.util.*;
import javax.net.ssl.*;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.xiaohui.faceapi.utils.Constants;

/**
 * @Description 通用http发送方法
 * @Author IT小辉同学
 * @Date 2023/06/01
 */
public class HttpUtils {
    
    
    private final static int CONNECT_TIME_OUT = 30000;
    private final static int READ_OUT_TIME = 50000;
    private static String boundaryString = getBoundary();
    private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);

    /**
     * @param url 发送请求的 URL
     * @return {@link String }
     * @Description 向指定 URL 发送GET方法的请求
     * @Author IT小辉同学
     * @Date 2023/06/01
     */
    public static String sendGet(String url) {
    
    
        return sendGet(url, StringUtils.EMPTY);
    }

    /**
     * @param url   发送请求的 URL
     * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
     * @return {@link String }
     * @Description 向指定 URL 发送GET方法的请求
     * @Author IT小辉同学
     * @Date 2023/06/01
     */
    public static String sendGet(String url, String param) {
    
    
        return sendGet(url, param, Constants.UTF8);
    }

    /**
     * @param url         发送请求的 URL
     * @param param       请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
     * @param contentType 编码类型
     * @return {@link String }
     * @Description 向指定 URL 发送GET方法的请求
     * @Author IT小辉同学
     * @Date 2023/06/01
     */
    public static String sendGet(String url, String param, String contentType) {
    
    
        StringBuilder result = new StringBuilder();
        BufferedReader in = null;
        try {
    
    
            String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url;
            log.info("sendGet - {}", urlNameString);
            URL realUrl = new URL(urlNameString);
            URLConnection connection = realUrl.openConnection();
            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            connection.connect();
            in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
            String line;
            while ((line = in.readLine()) != null) {
    
    
                result.append(line);
            }
            log.info("recv - {}", result);
        } catch (ConnectException e) {
    
    
            log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e);
        } catch (SocketTimeoutException e) {
    
    
            log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e);
        } catch (IOException e) {
    
    
            log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e);
        } catch (Exception e) {
    
    
            log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e);
        } finally {
    
    
            try {
    
    
                if (in != null) {
    
    
                    in.close();
                }
            } catch (Exception ex) {
    
    
                log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
            }
        }
        return result.toString();
    }

    /**
     * @param url   发送请求的 URL
     * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
     * @return {@link String }
     * @Description 向指定 URL 发送POST方法的请求
     * @Author IT小辉同学
     * @Date 2023/06/01
     */
    public static String sendPost(String url, String param) {
    
    
        PrintWriter out = null;
        BufferedReader in = null;
        StringBuilder result = new StringBuilder();
        try {
    
    
            log.info("sendPost - {}", url);
            URL realUrl = new URL(url);
            URLConnection conn = realUrl.openConnection();
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            conn.setRequestProperty("Accept-Charset", "utf-8");
            conn.setRequestProperty("contentType", "multipart/form-data;");
            conn.setDoOutput(true);
            conn.setDoInput(true);
            out = new PrintWriter(conn.getOutputStream());
            out.print(param);
            out.flush();
            in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
            String line;
            while ((line = in.readLine()) != null) {
    
    
                result.append(line);
            }
            log.info("recv - {}", result);
        } catch (ConnectException e) {
    
    
            log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e);
        } catch (SocketTimeoutException e) {
    
    
            log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e);
        } catch (IOException e) {
    
    
            log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e);
        } catch (Exception e) {
    
    
            log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e);
        } finally {
    
    
            try {
    
    
                if (out != null) {
    
    
                    out.close();
                }
                if (in != null) {
    
    
                    in.close();
                }
            } catch (IOException ex) {
    
    
                log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
            }
        }
        return result.toString();
    }

    /**
     * @param url     发送请求的 URL
     * @param param 参数
     * @return {@link String }
     * @Description 向指定 URL 发送POST方法的请求(url形式)
     * @Author IT小辉同学
     * @Date 2023/06/01
     */
    public static String sendSSLPost(String url, String param) {
    
    
        StringBuilder result = new StringBuilder();
        String urlNameString = url + "?" + param;
        try {
    
    
            log.info("sendSSLPost - {}", urlNameString);
            SSLContext sc = SSLContext.getInstance("SSL");
            sc.init(null, new TrustManager[]{
    
    new TrustAnyTrustManager()}, new java.security.SecureRandom());
            URL console = new URL(urlNameString);
            HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            conn.setRequestProperty("Accept-Charset", "utf-8");
            conn.setRequestProperty("contentType", "utf-8");
            conn.setDoOutput(true);
            conn.setDoInput(true);

            conn.setSSLSocketFactory(sc.getSocketFactory());
            conn.setHostnameVerifier(new TrustAnyHostnameVerifier());
            conn.connect();
            InputStream is = conn.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            String ret = "";
            while ((ret = br.readLine()) != null) {
    
    
                if (ret != null && !"".equals(ret.trim())) {
    
    
                    result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8));
                }
            }
            log.info("recv - {}", result);
            conn.disconnect();
            br.close();
        } catch (ConnectException e) {
    
    
            log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e);
        } catch (SocketTimeoutException e) {
    
    
            log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e);
        } catch (IOException e) {
    
    
            log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e);
        } catch (Exception e) {
    
    
            log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e);
        }
        return result.toString();
    }

    /**
     * @param url     发送请求的 URL
     * @param map     map集合
     * @param fileMap 文件映射
     * @return {@link byte[] }
     * @Description 向指定 URL 发送POST方法的请求(路径形式)
     * @Author IT小辉同学
     * @Date 2023/06/01
     */
    public static byte[] FilePost(String url, HashMap<String, String> map, HashMap<String, byte[]> fileMap) throws Exception {
    
    
        HttpURLConnection conne;
        URL url1 = new URL(url);
        conne = (HttpURLConnection) url1.openConnection();
        conne.setDoOutput(true);
        conne.setUseCaches(false);
        conne.setRequestMethod("POST");
        conne.setConnectTimeout(CONNECT_TIME_OUT);
        conne.setReadTimeout(READ_OUT_TIME);
        conne.setRequestProperty("accept", "*/*");
        conne.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundaryString);
        conne.setRequestProperty("connection", "Keep-Alive");
        conne.setRequestProperty("user-agent", "Mozilla/4.0 (compatible;MSIE 6.0;Windows NT 5.1;SV1)");
        DataOutputStream obos = new DataOutputStream(conne.getOutputStream());
        Iterator iter = map.entrySet().iterator();
        while (iter.hasNext()) {
    
    
            Map.Entry<String, String> entry = (Map.Entry) iter.next();
            String key = entry.getKey();
            String value = entry.getValue();
            obos.writeBytes("--" + boundaryString + "\r\n");
            obos.writeBytes("Content-Disposition: form-data; name=\"" + key
                    + "\"\r\n");
            obos.writeBytes("\r\n");
            obos.writeBytes(value + "\r\n");
        }
        if (fileMap != null && fileMap.size() > 0) {
    
    
            Iterator fileIter = fileMap.entrySet().iterator();
            while (fileIter.hasNext()) {
    
    
                Map.Entry<String, byte[]> fileEntry = (Map.Entry<String, byte[]>) fileIter.next();
                obos.writeBytes("--" + boundaryString + "\r\n");
                obos.writeBytes("Content-Disposition: form-data; name=\"" + fileEntry.getKey()
                        + "\"; filename=\"" + encode(" ") + "\"\r\n");
                obos.writeBytes("\r\n");
                obos.write(fileEntry.getValue());
                obos.writeBytes("\r\n");
            }
        }
        obos.writeBytes("--" + boundaryString + "--" + "\r\n");
        obos.writeBytes("\r\n");
        obos.flush();
        obos.close();
        InputStream ins = null;
        int code = conne.getResponseCode();
        try {
    
    
            if (code == 200) {
    
    
                ins = conne.getInputStream();
            } else {
    
    
                ins = conne.getErrorStream();
            }
        } catch (SSLException e) {
    
    
            e.printStackTrace();
            return new byte[0];
        }
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buff = new byte[4096];
        int len;
        while ((len = ins.read(buff)) != -1) {
    
    
            baos.write(buff, 0, len);
        }
        byte[] bytes = baos.toByteArray();
        ins.close();
        return bytes;
    }

    /**
     * @return {@link String }
     * @Description 分割线
     * @Author IT小辉同学
     * @Date 2023/06/01
     */
    private static String getBoundary() {
    
    
        StringBuilder sb = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < 32; ++i) {
    
    
            sb.append("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-".charAt(random.nextInt("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_".length())));
        }
        return sb.toString();
    }

    /**
     * @param value 传入的字符串
     * @return {@link String }
     * @Description 设置编码格式
     * @Author IT小辉同学
     * @Date 2023/06/01
     */
    private static String encode(String value) throws Exception {
    
    
        return URLEncoder.encode(value, "UTF-8");
    }

    /**
     * @param f 文件
     * @return {@link byte[] }
     * @Description 文件转换为二进制
     * @Author IT小辉同学
     * @Date 2023/06/01
     */
    public static byte[] getBytesFromFile(File f) {
    
    
        if (f == null) {
    
    
            return null;
        }
        try {
    
    
            FileInputStream stream = new FileInputStream(f);
            ByteArrayOutputStream out = new ByteArrayOutputStream(1000);
            byte[] b = new byte[1000];
            int n;
            while ((n = stream.read(b)) != -1)
                out.write(b, 0, n);
            stream.close();
            out.close();
            return out.toByteArray();
        } catch (IOException e) {
    
    
        }
        return null;
    }

    /**
     * @param jsonObject json对象
     * @return {@link String }
     * @Description 转换为url参数
     * @Author IT小辉同学
     * @Date 2023/06/01
     */
    public static String convertToUrlParams(JSONObject jsonObject) throws UnsupportedEncodingException {
    
    
        StringBuffer sb = new StringBuffer();
        Set<String> keys = jsonObject.keySet();
        for (String key : keys) {
    
    
            Object value = jsonObject.get(key);
            if (value instanceof String) {
    
    
                sb.append(key).append("=").append(URLEncoder.encode(value.toString(), "UTF-8"));
            } else if (value instanceof JSONObject) {
    
    
                sb.append(key).append("=").append(convertToUrlParams((JSONObject) value));
            } else if (value instanceof JSONArray) {
    
    
                sb.append(key).append("=").append(convertToUrlParams(((JSONArray) value).addObject()));
            }
            if (keys.size() != 1 || value == null) {
    
    
                sb.append("&");
            }
        }
        return sb.toString();
    }

    /**
     * @param fileName 文件名称
     * @return {@link byte[] }
     * @Description 读文件字节数组
     * @Author IT小辉同学
     * @Date 2023/06/01
     */
    public static byte[] readFileToByteArray(String fileName) throws IOException {
    
    
        File file = new File(fileName);
        FileInputStream inputStream = new FileInputStream(file);
        byte[] buffer = new byte[(int) file.length()];
        inputStream.read(buffer);
        inputStream.close();
        return buffer;
    }

    private static class TrustAnyTrustManager implements X509TrustManager {
    
    
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) {
    
    
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) {
    
    
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
    
    
            return new X509Certificate[]{
    
    };
        }
    }

    private static class TrustAnyHostnameVerifier implements HostnameVerifier {
    
    
        @Override
        public boolean verify(String hostname, SSLSession session) {
    
    
            return true;
        }
    }
}

接口调用实现

package com.xiaohui.faceapi.controller;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.xiaohui.faceapi.utils.AjaxResult;
import com.xiaohui.faceapi.utils.HttpUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;

@RestController
@RequestMapping("/api")
public class FaceApi {
    
    
    /**
     * url
     */
    private static String url = "https://api-cn.faceplusplus.com/facepp/v3/compare";
    /**
     * api密匙键
     */
    private static String api_key = "LP0v9dZj***oBKYgiwMq-P_CIk";
    /**
     * api秘钥值
     */
    private static String api_secret = "vg8UNGVMX****jmIpFw_wxaXTFCF4Xb5";

    /**
     * @param image1 image1
     * @param image2 image2
     * @return {@link AjaxResult }
     * @Description 根据URL进行人脸对比
     * @Author IT小辉同学
     * @Date 2023/06/01
     */
    @GetMapping("/faceUrl")
    public AjaxResult getFaceConfidenceByUrl(@RequestParam String image1, @RequestParam String image2) throws UnsupportedEncodingException {
    
    
        //数据填充
        HashMap params = new HashMap();
        params.put("api_key", api_key);
        params.put("api_secret", api_secret);
        params.put("image_url1", image1);
        params.put("image_url2", image2);
        //数据类型转换为object
        JSONObject jsonObject = new JSONObject(params);
        //转换为入参形式
        String param = HttpUtils.convertToUrlParams(jsonObject);
        //发起请求
        String getResult = HttpUtils.sendPost(url, param);
        //数据类型转换为object
        JSONObject result = JSONObject.parseObject(getResult);
        return AjaxResult.success(result);
    }

    /**
     * @param image1 image1
     * @param image2 image2
     * @return {@link AjaxResult }
     * @Description 根据文件流进行人脸对比
     * @Author IT小辉同学
     * @Date 2023/06/01
     */
    @GetMapping("/faceFile")
    public AjaxResult getFaceConfidenceByFile(@RequestParam(value = "image1") String image1, @RequestParam(value = "image2") String image2) throws Exception {
    
    
        File file1 = new File(image1);
        byte[] buff1 = HttpUtils.getBytesFromFile(file1);
        File file2 = new File(image2);
        byte[] buff2 = HttpUtils.getBytesFromFile(file2);
        HashMap<String, String> map = new HashMap<String, String>();
        HashMap<String, byte[]> byteMap = new HashMap<String, byte[]>();
        map.put("api_key", api_key);
        map.put("api_secret", api_secret);
        byteMap.put("image_file1", buff1);
        byteMap.put("image_file2", buff2);
        byte[] bacd = HttpUtils.FilePost(url, map, byteMap);
        String str = new String(bacd);
        JSONObject result = JSONObject.parseObject(str);
        return AjaxResult.success(result);
    }
}

五. 结果演示

URL请求方式
在这里插入图片描述
路径请求方式

在这里插入图片描述

Guess you like

Origin blog.csdn.net/weixin_53742691/article/details/130991969