人脸识别(Java+ Face++实现)

news2025/1/10 23:39:40

人脸识别(Java+ Face++实现)

在这里插入图片描述

一. 概述

Face++的核心技术是基于深度学习的人脸识别技术,其算法在准确率和速度方面都处于领先地位。该公司的产品和服务包括人脸识别SDK、人脸识别API、人脸比对服务、人脸检测服务、活体检测服务等。这些产品和服务广泛应用于金融、公安、零售、物流等领域。并且,Face++提供免费的API接口供开发者进行使用,所以,我们可以极小的成本借用该开放API完成我们对于项目的功能需求设计!

官网地址 https://www.faceplusplus.com.cn/

API文档地址 https://console.faceplusplus.com.cn/documents/4887586

image-20230601164002717

image-20230601160904624

二. 核心术语概念

人脸

人脸(Face)在人脸识别技术中特指图像中发现的人脸,当对一张图片进行人脸检测时,会将检测到的人脸记录下来,包括人脸在图片中的位置,用一个系统标识 face_token 来表示。注意:对同一张图片进行多次人脸检测,对同一个人脸会得到不同的 face_token。

人脸库

人脸库(FaceSet)是用来存储检测到人脸的存储对象。一个 FaceSet 内的 face_token 是不重复的。

人脸特征标识(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)

并发 (QPS) 指每秒可以发起的 API 请求次数。调用同一个功能模块下的各个 API ,会统一计算 QPS。例如人脸识别并发 (QPS) 为 10 个,人脸识别包括人脸检测 API、人脸比对 API、人脸搜索API、人脸库管理 API 组、获取人脸信息 API 和自定义人脸信息 API,则每秒可以发起 10 次 API 调用请求,不限制具体调用了哪一个 API。超过 10 次请求,将返回 403 并发超限错误码 (CONCURRENCY_LIMIT_EXCEEDED)。

三. 接口调用说明

描述

将两个人脸进行比对,来判断是否为同一个人,返回比对结果置信度和不同误识率下的阈值。

支持传入图片或 face_token 进行比对。使用图片时会自动选取图片中检测到人脸尺寸最大的一个人脸。

图片要求

图片格式:JPG(JPEG),PNG
图片像素尺寸:最小 4848 像素,最大 40964096 像素
图片文件大小:2MB
最小人脸像素尺寸: 系统能够检测到的人脸框为一个正方形,正方形边长的最小值为 150 像素。

调用 URL

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

调用方法

POST

请求体格式

multipart/form-data

权限

所有 API Key 都可以调用本 API。其中 face_rectangle1 和 face_rectangle2 参数只有正式 API Key 可以使用,试用 API Key 无法使用。

请求参数

是否必选参数名类型参数说明
必选api_keyString调用此 API 的 API Key
必选api_secretString调用此 API 的 API Secret
必选(四选一)face_token1String第一个人脸标识 face_token,优先使用该参数
image_url1String第一张图片的 URL
image_file1File第一张图片,二进制文件,需要用 post multipart/form-data 的方式上传。
image_base64_1Stringbase64 编码的二进制图片数据如果同时传入了 image_url1、image_file1 和 image_base64_1 参数,本 API 使用顺序为image_file1 优先,image_url1 最低。
必选(四选一)face_token2String第二个人脸标识 face_token,优先使用该参数
image_url2String第二张图片的 URL
image_file2File第二张图片,二进制文件,需要用 post multipart/form-data 的方式上传。
image_base64_2Stringbase64 编码的二进制图片数据如果同时传入了 image_url2、image_file2 和 image_base64_2 参数,本API 使用顺序为 image_file2优先,image_url2 最低。
可选(仅正式 API Key 可以使用)face_rectangle1String当传入图片进行人脸检测时,是否指定人脸框位置进行检测。如果此参数传入值为空,或不传入此参数,则不使用此功能。本 API 会自动检测图片内所有区域的所有人脸。如果使用正式 API Key 对此参数传入符合格式要求的值,则使用此功能。需要传入一个字符串代表人脸框位置,系统会根据此坐标对框内的图像进行人脸检测,以及人脸关键点和人脸属性等后续操作。系统返回的人脸矩形框位置会与传入的 face_rectangle 完全一致。对于此人脸框之外的区域,系统不会进行人脸检测,也不会返回任何其他的人脸信息。参数规格:四个正整数,用逗号分隔,依次代表人脸框左上角纵坐标(top),左上角横坐标(left),人脸框宽度(width),人脸框高度(height)。例如:70,80,100,100注:只有在传入 image_url1、image_file1 和 image_base64_1 三个参数中任意一个时,本参数才生效。
可选(仅正式 API Key 可以使用)face_rectangle2String当传入图片进行人脸检测时,是否指定人脸框位置进行检测。如果此参数传入值为空,或不传入此参数,则不使用此功能。本 API 会自动检测图片内所有区域的所有人脸。如果使用正式 API Key 对此参数传入符合格式要求的值,则使用此功能。需要传入一个字符串代表人脸框位置,系统会根据此坐标对框内的图像进行人脸检测,以及人脸关键点和人脸属性等后续操作。系统返回的人脸矩形框位置会与传入的 face_rectangle 完全一致。对于此人脸框之外的区域,系统不会进行人脸检测,也不会返回任何其他的人脸信息。参数规格:四个正整数,用逗号分隔,依次代表人脸框左上角纵坐标(top),左上角横坐标(left),人脸框宽度(width),人脸框高度(height)。例如:70,80,100,100注:只有在传入image_url2、image_file2 和image_base64_2 三个参数中任意一个后本参数才生效。

返回值说明

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

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

字段类型说明
face_tokenString人脸的标识
face_rectangleObject人脸矩形框的位置,包括以下属性。每个属性的值都是整数: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状态代码错误信息说明
400INVALID_FACE_TOKEN: <face_token>使用face_token作为参数时,所传的face_token不存在。
400IMAGE_ERROR_UNSUPPORTED_FORMAT: 参数对应的图像无法正确解析,有可能不是一个图像文件、或有数据破损。
400INVALID_IMAGE_SIZE: 参数对应的客户上传的图像像素尺寸太大或太小,图片要求请参照本API描述。对应图像太大的那个参数的名称
400INVALID_IMAGE_URL: 无法从参数对应的image_url下载图片,图片URL错误或者无效。
400IMAGE_FILE_TOO_LARGE: 参数对应的客户上传的图像文件太大。本 API 要求图片文件大小不超过 2 MB
403INSUFFICIENT_PERMISSION:试用 API Key 无法使用 对应的参数。请勿传入此参数。或者使用正式 API Key 调用。
412IMAGE_DOWNLOAD_TIMEOUT: 下载图片超时。

通用的ERROR_MESSAGE

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

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/599168.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在树莓派3B+上安装Pytorch1.7

在树莓派3B上安装Pytorch1.7(应该是最简单的方法了)_package libopenblas-dev has no installation cand_Chauncey_Wang的博客-CSDN博客由于项目要求&#xff0c;我需要在树莓派上安装pytorch这就有几个问题&#xff0c;首先吧&#xff0c;咱们和外面之间有一道长城&#xff0c…

计算机网络 七大性能指标【速率】【带宽】【吞吐量】【时延】【时延带宽积】【往返时间】【利用率】

计算机网络 速率&#xff08;bit/s 数据的传送速率&#xff09;带宽&#xff08;频域-频带宽度&#xff0c;时域-最高速率&#xff09;吞吐量&#xff08;单位时间的 数据量&#xff09;时延&#xff08;一端传送到另一端所需的时间&#xff09;1. 发送时延&#xff08;发送所用…

来自6种编程语言的祝福:欢乐六一儿童节

六一儿童节的由来是为了纪念在法西斯侵略战争中死难的儿童&#xff0c;反对帝国主义的虐杀和毒害儿童&#xff0c;保障儿童权利。1949年11月&#xff0c;国际民主妇女联合会在莫斯科召开大会&#xff0c;决定每年的6月1日为全世界少年儿童的节日&#xff0c;即国际儿童节。 六一…

RPC(1):软件项目架构变化简述

1单体架构 1.1架构图 单体架构就是一个项目里面包含这个项目中全部代码。一个应用搞定全部功能。 DNS 服务器可以是单映射&#xff0c;也可以配置多个映射。 1.2软件代码结构 在单体架构项目中&#xff0c;团队都是通过包(package)进行区分每个模块。 总体包结构&#xff…

Android进阶 :实现自定义View

Android进阶&#xff1a;实现自定义View 导语 有时候我们会想要实现一些复杂或者是独特的组件效果&#xff0c;这时候系统提供的组件可能不能满足我们的需求&#xff0c;这个时候我们一般就会有两个解决办法&#xff0c;一是上网查找开源的控件库&#xff0c;一些流行的开源库…

【JUnit技术专题】「入门到精通系列」手把手+零基础带你玩转单元测试,让你的代码更加“强壮”(夯实功底篇)

手把手零基础带你玩转单元测试&#xff0c;让你的代码更加“强壮” 前言介绍JUnit是什么&#xff1f;JUnit和xUnit之间的关系 JUnit的基本概念JUnit的特点什么是一个单元测试用例 JUnit的用法JUnit的最佳实践案例分析创建一个类创建 Test Case 类创建 Test Runner 类 JUnit总体…

Web实验二 CSS基本样式实验

实验原理 通过创建CSS样式文件&#xff0c;理解CSS样式基本属性作用及意义。 实验目的 理解CSS基本概念及功能 理解CSS样式的设计原则 理解并掌握CSS样式的基本声明方法 理解并掌握多种CSS选择器的使用方法 理解并掌握字文本、表格、超链接等元素常用属性的使用方法 理解并掌握…

机器人学:DH参数总结(传统DH方法和改进DH方法)

1. 传统DH参数方法 1.1 确定坐标系的方法 定义&#xff1a;杆 i i i的近端是关节 i i i&#xff0c;远端是关节 i 1 i1 i1. 【下面的规则参考上面的图看得更清楚】 对于 n n n自由度机器人&#xff0c;可用以下步骤建立与各杆件 i ( i 0 , 1 , … , n ) i(i0,1,…,n) i(i0,…

【iOS底层探索- Block原理分析-循环引用】

文章目录 前言准备工作1. Block的分类2. Block的内存分析捕获外部变量引用计数的变化堆栈释放的差异总结 3. Block的循环引用3.1 什么是循环引用&#xff1f;案例引入 循环引用解决方法1. 强弱共舞2. 手动中断循环引用3. 参数形式解决循环引用&#xff08;block传参&#xff09…

汇编指令执行过程及CS与IP和DS寄存器关系与变化

内存指令及寄存器初始值: CS:2000H IP:0 DS:1000H AX:0 BX:0 上面在内存中的汇编指令是如何执行的? 验证: 在debug下用a指令先向内存写入下面指令,然后用u指令查看 mov ax,2000 mov ds,ax mov ax,[0008] mov ax,[0002] 在debug下用a指令先向内存写入下面指令,然后用u指…

Computer之Compilation:Cmake的简介、安装、案例应用之详细攻略

Computer之Compilation&#xff1a;Cmake的简介、安装、案例应用之详细攻略 目录 Cmake的简介 Cmake的安装 1、官方下载 2、执行安装程序&#xff0c;并按照提示进行安装 3、验证测试 Cmake的案例应用 Cmake的简介 CMake&#xff08;Cross-platform Make&#xff09;是一…

【嵌入式烧录/刷写文件】-1.7-将一个文本文件转换为Motorola S-record(S19/SREC/mot/SX)文件

案例背景(共5页精讲)&#xff1a; 有如下两个文本文件&#xff08;*.txt&#xff0c;*.ini&#xff0c;*.asc…&#xff09;转换成Motorola S-record(S19/SREC/mot/SX)文件。常用于Key密钥&#xff0c;signature签名…的导入&#xff0c;或对一段数据计算出hex记录的最后一个字…

服务器性能优化方法

文章目录 服务器性能优化方法什么是服务器并发处理能力&#xff1f;什么方法衡量服务器的并发能力&#xff1f;怎么提高服务器的并发处理能力&#xff1f;**1&#xff0c;提高CPU并发计算能力**&#xff08;1&#xff09;多进程&多线程&#xff08;2&#xff09;减少进程切…

spring cloud搭建(eureka)

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习新东西是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习…

力扣高频SQL50题(基础版)——第二天

力扣高频SQL50题(基础版)——第二天 1 文章浏览Ⅰ 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 1.2 示例sql语句 # Write your MySQL query statement below SELECT distinct author_id id FROM Views WHERE author_idviewer_id ORDER BY id asc1.3 运行截图 2 无…

基于MSP430送药小车 ----- 基础篇【2021年全国电赛(F题)】

文章目录 一、赛题1. 任务2. 要求3. 说明 二、构思 分析1. 引脚利用2. PID算法3. 灰度循迹及标志位4. 视觉模块5. 直角转弯、原地转向 三、硬件清单四、逻辑设计1. 近端送药2. 中端送药3. 远端送药 五、程序设计1. OpenMV2. 灰度循迹3. 装药卸药 总结 一、赛题 1. 任务 设计并…

Linux Socket 分包 和 粘包 问题 - 解决方案

分包和粘包在TCP网络编程中是非常常见的&#xff0c;分包会造成 接收端 接收的数据不全的问题&#xff0c;粘包会造成接收多余的数据的文件。 这里做一个举例&#xff0c;例如客户端A要发送字符串“helloworld”给服务器B&#xff0c;A是一次性发送&#xff0c;但TCP有可能会将…

A Unified Conditional Framework for Diffusion-based Image Restoration

A Unified Conditional Framework for Diffusion-based Image Restoration (Paper reading) Yi Zhang, CUHK, CN, arXiv2023, Cited:0, Code, Paper 1. 前言 最近&#xff0c;扩散概率模型&#xff08;Diffusion Probabilistic Models&#xff0c;DPMs&#xff09;在图像生成…

Android 自定义View 之 饼状进度条

饼状进度条 前言正文一、XML样式二、构造方法三、测量四、绘制① 绘制描边① 绘制进度 五、API方法六、使用七、源码 前言 前面写了圆环进度条&#xff0c;这次我们来写一个饼状进度条&#xff0c;首先看一下效果图&#xff1a; 正文 效果图感觉怎么样呢&#xff1f;下面我们…

GLTF/GLB模型轻量化简明教程

GLB 文件格式很方便&#xff0c;因为它包含渲染所需的所有文件&#xff0c;包括纹理。 但是&#xff0c;根据用途&#xff0c;你可能希望简化文件&#xff0c;因为它有时非常详细。 在本文中&#xff0c;我将使用 gltf-transform 来执行简化&#xff0c;并且假设你使用的是 Wi…