目录
1、七牛云内容审核介绍
2、查看内容审核官方文档
2.1、文本内容审核
2.1.1、文本内容审核的请求示例
2.1.2、文本内容审核的返回示例
2.2、图片内容审核
2.2.1、请求参数
2.2.2、返回参数
2.3、视频内容审核
3、代码实现
3.1、前期代码准备
3.2、文本内容审核
3.3、图片内容审核
3.4、视频内容审核
1、七牛云内容审核介绍
在之前做的一个项目中,用了七牛云的内容审核API,感觉很有意思。所以在这里写一下在JavaWeb项目中怎么使用七牛云的内容审核功能。
七牛云的内容审核服务是一套综合性的解决方案,旨在帮助用户在不同的应用场景中实现对多媒体内容的智能审核和管理。无论是在社交平台、直播、短视频、在线社区,还是在电商、教育等领域,七牛云的审核服务都能提供全面的支持,确保用户上传、存储、分发或展示的内容符合合规要求。
在这篇博客中主要是探讨对文本、图片和视频这三类的内容审核在java中的实现。
2、查看内容审核官方文档
在七牛云的官方文档中,详细的写了相关内容审核API的【使用限制、功能描述、请求参数、返回参数】等。还有相关的代码示例提供参考。
在这些文档中,我们最需要了解的是怎么正确的去请求我们想使用的API,并接收API返回的结果。
这个大概的代码思路就是,不管是文本、图片还是视频的审核,我们都需要创建HTTP请求来发送请求到相应的API,然后再接收API审核后的结果。在这个结果中就有对每一种场景审核的详细信息,这三种审核的结果还是有些差异的,所以放到后面详细讨论。
请求又分为了请求头和请求体。
在请求头部分:这三种审核除了各自需要访问的七牛云内部接口不同,其他参数都是一样的。
文本内容审核访问内部接口:v3/text/censor
图片内容审核访问内部接口:/v3/image/censor
视频内容审核访问内部接口:/v3/video/censor
以文本内容审核请求头为例:
POST /v3/text/censor HTTP/1.1
Host: ai.qiniuapi.com
Content-Type: application/json
Authorization: Qiniu YnxrxOSvGotRZpqkZnMzl_euuoqRzOKUd6zwbRju:r6a-o2UpBg6A4puIMnkCExH8lE8=
对请求头的解释:
POST:请求方式
/v3/text/censor:七牛云内容审核 API 的一个具体端点路径,用于调用文本内容的审核功能
HTTP/1.1:网络协议
Host: ai.qiniuapi.com:域名,这个域名是七牛云提供的一个特定服务入口,专门处理与人工智能相关的 API 请求,包括文本、图片、视频等内容的审核
Content-Type: application/json:告诉服务器请求的消息主体(body)中包含的数据类型。在这个请求中,它指示消息主体的内容是 JSON 格式。
Authorization: Qiniu YnxrxOSvGotRZpqkZnMzl_euuoqRzOKUd6zwbRju:r6a-o2UpBg6A4puIMnkCExH8lE8=
Authorization是 HTTP 头字段,用于携带认证信息,让服务器知道请求来自哪里,以及它是否有权限执行该请求。
Qiniu YnxrxOSvGotRZpqkZnMzl_euuoqRzOKUd6zwbRju:r6a-o2UpBg6A4puIMnkCExH8lE8= 是将
AccessKey
和签名字符串通过冒号:
拼接在一起,形成最终的认证字符串,作为Authorization
头的值发送给服务器
2.1、文本内容审核
文本内容审核官方文档:API调用文本审核_API 文档_内容审核 - 七牛开发者中心 (qiniu.com)
2.1.1、文本内容审核的请求示例
POST /v3/text/censor HTTP/1.1
Host: ai.qiniuapi.com
Content-Type: application/json
Authorization: Qiniu YnxrxOSvGotRZpqkZnMzl_euuoqRzOKUd6zwbRju:r6a-o2UpBg6A4puIMnkCExH8lE8=
{
"data": {
"text": "七牛文本审核示例"
},
"params": {
"scenes": [
"antispam"
]
}
}
{ }里面的就是请求体了,在这个示例当中的请求体是JSON格式的。
其中的 "data"是需要审核的内容,"params" 是审核类型
文本内容审核支持的审核类型只有 antispam,其对应的label如下:
2.1.2、文本内容审核的返回示例
{
"message": "OK",
"code": 200,
"result": {
"scenes": {
"antispam": {
...... // 详细信息
}
},
"suggestion": "pass"
}
}
上面示例中结果的字段含义如下:
2.2、图片内容审核
图片内容审核官方文档:API调用图片审核_API 文档_内容审核 - 七牛开发者中心 (qiniu.com)
下面是这个个文档中主要的几个部分
2.2.1、请求参数
请求参数有两个字段,分别是代表需要进行审核的图片资源访问路径(data.uri)和 代表需要审核哪些类型的场景(params.scenes),例如:图片鉴黄(pulp)、图片鉴暴恐(terror)、图片敏感人物识别(politician)等。更多的场景类型可以从官方文档了解
具体信息如下:
请求示例:
POST /v3/image/censor HTTP/1.1
Host: ai.qiniuapi.com
Content-Type: application/json
Authorization: Qiniu YnxrxOSvGotRZpqkZnMzl_euuoqRzOKUd6zwbRju:r6a-o2UpBg6A4puIMnkCExH8lE8=
{
"data": {
"uri": "https://mars-assets.qnssl.com/resource/gogopher.jpg"
},
"params": {
"scenes": [
"pulp",
"terror",
"politician"
]
}
}
在这个请求示例中:
需要进行审核的图片访问路径就是:"https://mars-assets.qnssl.com/resource/gogopher.jpg"
而选择的审核场景类型是:"pulp" , "terror" , "politician"
图片内容审核的API在接收到我们的这个请求后,就会去对这个指定路径的图片进行审核,并且只会审核这个图片有没有涉及黄色、涉及暴恐、涉及敏感人物
2.2.2、返回参数
返回示例:
{
"message": "OK",
"code": 200,
"result": {
"scenes": {
"terror": {
......// 详细信息
},
"politician": {
......// 详细信息
},
"pulp": {
......// 详细信息
}
},
"suggestion": "pass"
}
}
1、result.suggestion:这是对这张图片审核后,API给出的最终建议,主要的取值为 通过(pass)、人工复核(review)、违规(block)这三种。
2、result.scenes:这里面包含了对每种场景审核后的详细信息
2.3、视频内容审核
视频内容审核官方文档:API调用视频审核_API 文档_内容审核 - 七牛开发者中心 (qiniu.com)
视频审核的场景类型和图片审核的差不多,这里就不讲了。主要还是讲一下请求体种携带的参数。
请求示例中的请求体:解释直接以注释的形式写在代码中了
{
"data": {
// uri是视频文件的URL地址。这个URI指向你希望审核的具体视频文件
"uri": "https://mars-assets.qnssl.com/scene.mp4",
// id是该次视频审核请求的唯一标识符。这是一个自定义参数,
// 用于跟踪和管理审核任务。这个参数不是必选的,如果你没有指定,他会自己生成一个唯一标识返回给你
"id": "video_censor_test"
},
"params": {
// scenes是要审核的场景类型列表。每个场景代表一个审核类别,系统会根据这些场景进行相应的内容检查
"scenes": [
"pulp",
"terror",
"politician"
],
// cut_params是视频截帧参数,用于配置视频在审核过程中如何进行截帧分析。
// 通过这个参数,可以控制视频审核的截帧频率
"cut_param": {
"interval_msecs": 5000
}
}
}
当我们创建好请求并发送给视频内容审核的API后,他会立即返回这次审核任务的唯一标识符 "job_id" ,视频审核结果需要通过job_id获得。因为视频审核的时间是取决于视频的时长以及视频审核的截帧频率,所以时间会比较长,那么就需要以异步或者是循环等待的方式通过"job_id"去获取视频审核的结果。
通过"job_id"去获取视频审核的结果的请求如下:请求路径就是 "/v3/jobs/video/" 再加上 "job-id"
GET /v3/jobs/video/<job-id> HTTP/1.1
Host: ai.qiniuapi.com
Authorization: Qiniu <AccessKey>:<Sign>
通过"job_id"去获取视频审核的返回结果示例:
{
"status": "FINISHED", // 这是任务的当前状态
"rescheduled_at": "2019-02-22T20:15:49.931+08:00", // 这是任务被重新调度的时间戳
"created_at": "2019-02-22T20:15:49.931+08:00",
"request": {
...... // 请求信息
},
"updated_at": "2019-02-22T20:15:52.426+08:00",
"result": {
"message": "OK",
"code": 200,
"result": {
"scenes": {
"terror": {
...... // 详细信息
},
"politician": {
...... // 详细信息
},
"pulp": {
...... // 详细信息
},
"suggestion": "pass"
}
},
"id": "5c6fe7f59a0b0500082b774f" // 唯一标识符,标识这个特定的视频内容审核任务
}
在获取返回结果后,判断结果中的 "status" 是否是 ‘
FINISHED
’ ,当状态为FINISHED
时,你可以查看并使用响应中的审核结果。最后再判断视频内容审核API对此次进行审核的视频给出的建议。
3、代码实现
3.1、前期代码准备
首先需要在项目的pom文件中引入七牛云的依赖
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>7.12.0</version>
</dependency>
然后需要创建一个类七牛云的相关配置,比如公钥(access-key)和私钥(secret-key)
import com.qiniu.util.Auth;
import com.qiniu.util.StringMap;
import lombok.Data;
import org.springframework.stereotype.Component;
/**
* @Author 小涛Tao
* @Date: 2024/05/29/20:04
* version 1.0 注释:七牛云配置
**/
@Data
@Component
public class QiNiuConfig {
// 公钥(AK)
private String accessKey = "填自己的";
// 密钥(SK)
private String secretKey = "填自己的";
// 对象存储空间
private String bucketName = "填自己的";
/**
* Auth 类的用途
* Auth 类的主要用途包括:
* (1)生成上传凭证:在上传文件到七牛云时,需要生成上传凭证(token),以便七牛云验证请求的合法性。
* (2)生成下载凭证:在下载受保护的资源时,需要生成下载凭证,以便七牛云验证请求的合法性。
* (3)生成请求签名:在调用七牛云 API 时,需要对请求进行签名,以便七牛云验证请求的合法性。
*
* @return 通过AK和SK生成的Auth
*/
public Auth buildAuth() {
String accessKey = this.getAccessKey();
String secretKey = this.getSecretKey();
return Auth.create(accessKey, secretKey);
}
/**
* 获取包含认证信息的令牌 token ,来访问参数中的 url 这个路径, 并且只能用来访问这个 API
* @param url 要访问的 API
* @param method 请求的方法
* @param body 请求主体
* @param contentType 表示请求主体的数据格式,比如 JSON 格式
* @return
*/
public String getToken(String url, String method, String body, String contentType) {
final Auth auth = buildAuth();
String qiniuToken = "Qiniu " + auth.signQiniuAuthorization(url, method, body == null ? null : body.getBytes(), contentType);
return qiniuToken;
}
}
创建一个公共的父类,包含文本、图片、视频这三种审核共同需要用到的属性等。
import com.example.lt.config.QiNiuConfig;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @Author 小涛Tao
* @Date: 2024/05/29/20:25
* version 1.0 注释:三种审核方式的父类
*
**/
@Service
public class AuditService<T,R> {
@Autowired
protected QiNiuConfig qiNiuConfig;
// DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES: 这是 Jackson 的一个配置选项,控制在反序列化过程中遇到未知属性(即 JSON 中存在而目标 Java 类中没有定义的属性)时的行为。
// false: 将这个选项设置为 false 表示在反序列化过程中,如果 JSON 中存在未知属性,Jackson 不会抛出异常,而是忽略这些属性。默认情况下,这个选项是 true,即如果遇到未知属性会抛出 UnrecognizedPropertyException 异常。
protected ObjectMapper objectMapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// 请求体内容的数据结构类型,比如:JSON 数据结构
static final String contentType = "application/json";
}
创建接收审核结果的类,根据官方文档中返回结果示例内的字段来创建
(1)ResultChildJson类,用于封装 "result" 下的 "scenes" 和 "suggestion"
@Data
@ToString
public class ResultChildJson implements Serializable {
String suggestion; // 建议
ScenesJson scenes; // 场景 scenes 下包含具体的审核类型(场景)结果
}
文本内容审核返回示例
图片内容审核返回示例
(2)ResultJson类,用于封装状态码、结果体等信息
@Data
@ToString
public class ResultJson implements Serializable {
Integer code; // 状态码
String message; // 提示信息
ResultChildJson resultChile; // 结果体 resultChile
}
视频内容审核返回示例
(3)BodyJson类,用于封装视频内容审核的结果
@Data
@ToString
public class BodyJson implements Serializable {
String id; // 这次视频审核请求任务的唯一标识符
String status; // 这个审核任务的状态,有没有完成
ResultJson result; // 结果JSON结果中的 "result"
}
视频内容审核返回示例
3.2、文本内容审核
直接给代码了,代码中有注释
/**
* @Author 小涛Tao
* @Date: 2024/05/29/21:08
* version 1.0 注释:文本内容审核
**/
@Service
public class TextAuditService extends AuditService<String, Integer>{
// 文本内容审核 API 地址
static String textUrl = "http://ai.qiniuapi.com/v3/text/censor";
// 根据官方文档的示例来自定义请求体
static String textBody = "{\n" +
" \"data\": {\n" +
" \"text\": \"${text}\"\n" +
" },\n" +
" \"params\": {\n" +
" \"scenes\": [\n" +
" \"antispam\"\n" +
" ]\n" +
" }\n" +
"}";
/**
* 暴露出去的文本审核方法
* @param text 需要进行审核的文本
*
* @return 0:代表审核通过 1:代表审核不通过
*/
@Override
public Integer audit(String text) {
Integer auditState = 0; // 暂时先设置为审核成功
String body = textBody.replace("${text}", text);// 把需要审核的文本放入请求体数据部分中
String method = "POST";
// 获取包含认证信息的 token,调用 qiNiuConfig 中的 getToken 方法获取
final String token = qiNiuConfig.getToken(textUrl, method, body, contentType);
// 设置访问审核文本 API 的请求头
StringMap header = new StringMap();
header.put("Host", "ai.qiniuapi.com");
header.put("Authorization", token);
header.put("Content-Type", contentType);
Configuration cfg = new Configuration(Region.region2());
final Client client = new Client(cfg);
try {
Response response = client.post(textUrl, body.getBytes(), header, contentType);
// 获取响应的详细信息并按照 "\n" 进行分割,然后获取第三部分,因为文本审核结果中的 result 就是第三部分
// 然后通过 objectMapper 将字符串转换成 Map 对象
final Map map = objectMapper.readValue(response.getInfo().split("\n")[2], Map.class);
// 将 map 中 key 为 "result" 的值转换成 ResultChildJson.class 类型的对象
final ResultChildJson resultChild = objectMapper.convertValue(map.get("result"), ResultChildJson.class);
// 文本审核直接审核 suggestion ,如果返回结果中的 suggestion 不是 pass 的话,就是不通过
if (!resultChild.getSuggestion().equals("pass")) {
auditState = 1; // 设置审核结果为不通过
}
} catch (Exception e) {
e.printStackTrace();
}
// 返回审核结果
return auditState;
}
}
3.3、图片内容审核
/**
* @Author 小涛Tao
* @Date: 2024/05/30/14:44
* version 1.0 注释:图片内容审核
**/
@Service
public class ImageAuditService extends AuditService<String, Ingeter> {
// 图片的内容审核 API 地址
static String imageUlr = "http://ai.qiniuapi.com/v3/image/censor";
// 根据官方文档的示例来自定义请求体
static String imageBody = "{\n" +
" \"data\": {\n" +
" \"uri\": \"${url}\"\n" +
" },\n" +
" \"params\": {\n" +
" \"scenes\": [\n" +
" \"pulp\",\n" +
" \"terror\",\n" +
" \"politician\"\n" +
" ]\n" +
" }\n" +
"}";
/**
* 暴露出去的图片审核方法
* @param url 需要进行审核的图片资源路径
*
* @return 0:代表审核通过 1:代表审核不通过
*/
@Override
public Ingeter audit(String url) {
Integer auditState = 0; // 暂时先设置为审核成功
try {
String body = imageBody.replace("${url}", url); // 把自定义请求体中的 $url 替换成 url 参数
String method = "POST";
// 获取token,这个token只能用于请求 imageUlr ,且请求方法是形参 method 真实的方法,请求体也是必须是形参 body 一样,还有内容类型 contentType
final String token = qiNiuConfig.getToken(imageUlr, method, body, contentType);
StringMap header = new StringMap();
header.put("Host", "ai.qiniuapi.com");
header.put("Authorization", token); // 设置 Authorization 为包含认证信息的 token
header.put("Content-Type", contentType);
Configuration cfg = new Configuration(Region.region2());
final Client client = new Client(cfg);
Response response = client.post(imageUlr, body.getBytes(), header, contentType);
// 获取响应的详细信息并按照 "\n" 进行分割,然后获取第三部分,因为图片审核结果中的 result 是第三部分
// 然后通过 objectMapper 将字符串转换成 Map 对象
final Map map = objectMapper.readValue(response.getInfo().split("\n")[2], Map.class);
final ResultChildJson result = objectMapper.convertValue(map.get("result"), ResultChildJson.class);
// 直接判断 suggestion ,如果返回结果中的 suggestion 不是 pass 的话,就是不通过
if (!resultChild.getSuggestion().equals("pass")) {
auditState = 1; // 设置审核结果为不通过
}
} catch (Exception e) {
e.printStackTrace();
}
return auditState;
}
}
3.4、视频内容审核
/**
* @Author 小涛Tao
* @Date: 2024/05/30/14:56
* version 1.0 注释:视频审核
**/
@Service
public class VideoAuditService extends AbstractAuditService<String, AuditResponse> {
// 视频内容审核 API 地址
static String videoUrl = "http://ai.qiniuapi.com/v3/video/censor";
// 根据官方文档的示例来自定义请求体
static String videoBody = "{\n" +
" \"data\": {\n" +
" \"uri\": \"${url}\",\n" +
" \"id\": \"video_censor_test\"\n" +
" },\n" +
" \"params\": {\n" +
" \"scenes\": [\n" +
" \"pulp\",\n" +
" \"terror\",\n" +
" \"politician\"\n" +
" ],\n" +
" \"cut_param\": {\n" +
" \"interval_msecs\": 5000\n" +
" }\n" +
" }\n" +
"}";
/**
* 暴露出去的视频审核方法
* @param url 需要进行审核的视频资源路径
*
* @return 0:代表审核通过 1:代表审核不通过
*/
@Override
public AuditResponse audit(String url) {
Integer auditState = 0; // 暂时先设置为审核成功
String body = videoBody.replace("${url}", url);
String method = "POST";
// 获取包含认证信息的 token
final String token = qiNiuConfig.getToken(url, method, body, contentType);
StringMap header = new StringMap();
header.put("Host", "ai.qiniuapi.com");
header.put("Content-Type", contentType);
header.put("Authorization", token);
final Configuration config = new Configuration(Region.region2());
final Client client = new Client(config);
try {
final Response response = client.post(videoUrl, body.getBytes(), header, contentType);
// 解析返回结果,里面有这次视频审核任务的唯一 id(job-id)
final Map map = objectMapper.readValue(response.getInfo().split("\n")[2], Map.class);
final Object job = map.get("job");
// 生成访问这次视频审核任务的 url
url = "http://ai.qiniuapi.com/v3/jobs/video/" + job.toString();
method = "GET";
header = new StringMap();
header.put("Host", "ai.qiniuapi.com");
// 获取包含认证信息的 token,并放入请求头中的 "Authorization"
header.put("Authorization", qiNiuConfig.getToken(url, method, null, null));
// 获取视频审核结果
while (true) {
Response response1 = client.get(url, header);
final BodyJson bodyJson = objectMapper.readValue(response1.getInfo().split("\n")[2], BodyJson.class);
// 如果从七牛云获取到的审核结果的状态【status】不是 “FINISHED”,则不进行校验(audit)方法,而是等待 2 秒再去获取一次,知直到状态为 “FINISHED”
if (bodyJson.getStatus().equals("FINISHED")) {
// 直接判断 suggestion ,如果返回结果中的 suggestion 不是 pass 的话,就是不通过
if (!bodyJson.getResult().getResultChile().getSuggestion().equals("pass")) {
auditState = 1; // 设置审核结果为不通过
}
return auditState; // 返回审核结果
}
// 否则就等待两秒,然后再去获取并判断审核结果的状态【status】是不是 “FINISHED”
Thread.sleep(2000L);
}
} catch (Exception e) {
e.printStackTrace();
}
return auditState; // 返回审核结果
}
}
后续可以再将这些代码该进一下,使得对内容审核的判断更加的灵活 !
推荐:
【Spring】Spring AOP底层原理前置知识:代理模式中的静态代理-CSDN博客https://blog.csdn.net/m0_65277261/article/details/139106845?spm=1001.2014.3001.5501
【Spring】Spring AOP底层原理:JDK动态代理和CGLIB动态代理_spring cglib和jdk动态代理-CSDN博客https://blog.csdn.net/m0_65277261/article/details/139243897?spm=1001.2014.3001.5501【Spring】RestTemplete工具类_resttemplate工具类-CSDN博客https://blog.csdn.net/m0_65277261/article/details/140543400?spm=1001.2014.3001.5501