因为黑马头条用的是阿里云的内容安全1.0接口,只有企业用户才能使用,所以我们只能改装他的审核工具类,使用阿里云的内容安全增强版的接口来实现图片审核。
为了能完全适配黑马头条项目的接口规范,我对官网提供的JAVA案例进行了改动,官网的SDK图片上传方式有三种:
- 使用公网url,这一点我们不符合,因为如果我们使用虚拟机的minio,那只能在局域网访问;
- 使用OSS云服务,开通云存储服务,将文件搬到阿里云上再审核,这样比较方便,但和黑马头条的技术栈不匹配。
- 本地上传(本质上还是临时上传到OSS上,再审核,不过不需要我们开通OSS服务,比较方便),但是上传方式和黑马头条不同,这里的SDK上传的传入值是文件在本地的路径,黑马是byteArray字节数组。
综合考量,改动方式3,在官网测试案例的基础上,将 fileStorageService 得到的字节数组转换为本地的文件,再将文件路径作为参数上传文件至阿里云,实现如下:
Pom依赖:
<!--安装内容检测SDK-->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>green20220302</artifactId>
<version>2.1.0</version>
</dependency>
<!--安装OSS SDK-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.16.3</version>
</dependency>
GreenImageScan工具类:
package com.heima.common.aliyun;
import com.alibaba.fastjson.JSON;
import com.aliyun.green20220302.Client;
import com.aliyun.green20220302.models.DescribeUploadTokenResponse;
import com.aliyun.green20220302.models.DescribeUploadTokenResponseBody;
import com.aliyun.green20220302.models.ImageModerationRequest;
import com.aliyun.green20220302.models.ImageModerationResponse;
import com.aliyun.green20220302.models.ImageModerationResponseBody;
import com.aliyun.green20220302.models.ImageModerationResponseBody.ImageModerationResponseBodyData;
import com.aliyun.green20220302.models.ImageModerationResponseBody.ImageModerationResponseBodyDataResult;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.models.RuntimeOptions;
import io.minio.ObjectStat;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.FileOutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@Getter
@Setter
@Component
@ConfigurationProperties(prefix = "aliyun")
public class GreenImageScan {
private String accessKeyId;
private String secret;
private String imageService;
private String endpoint;
//服务是否部署在vpc上
public boolean isVPC = false;
//文件上传token endpoint->token
public Map<String, DescribeUploadTokenResponseBody.DescribeUploadTokenResponseBodyData> tokenMap = new HashMap<>();
//上传文件请求客户端
public OSS ossClient = null;
/**
* 创建请求客户端
*
* @param accessKeyId
* @param secret
* @param endpoint
* @return
* @throws Exception
*/
public Client createClient(String accessKeyId, String secret, String endpoint) throws Exception {
Config config = new Config();
config.setAccessKeyId(accessKeyId);
config.setAccessKeySecret(secret);
// 设置http代理。
// config.setHttpProxy("http://10.10.xx.xx:xxxx");
// 设置https代理。
// config.setHttpsProxy("https://10.10.xx.xx:xxxx");
// 接入区域和地址请根据实际情况修改
config.setEndpoint(endpoint);
return new Client(config);
}
/**
* 创建上传文件请求客户端
*
* @param tokenData
* @param isVPC
*/
public void getOssClient(DescribeUploadTokenResponseBody.DescribeUploadTokenResponseBodyData tokenData, boolean isVPC) {
//注意,此处实例化的client请尽可能重复使用,避免重复建立连接,提升检测性能。
if (isVPC) {
ossClient = new OSSClientBuilder().build(tokenData.ossInternalEndPoint, tokenData.getAccessKeyId(), tokenData.getAccessKeySecret(), tokenData.getSecurityToken());
} else {
ossClient = new OSSClientBuilder().build(tokenData.ossInternetEndPoint, tokenData.getAccessKeyId(), tokenData.getAccessKeySecret(), tokenData.getSecurityToken());
}
}
/**
* 上传文件
*
* @param filePath
* @param tokenData
* @return
* @throws Exception
*/
public String uploadFile(String filePath, DescribeUploadTokenResponseBody.DescribeUploadTokenResponseBodyData tokenData) throws Exception {
String[] split = filePath.split("\\.");
String objectName;
if (split.length > 1) {
objectName = tokenData.getFileNamePrefix() + UUID.randomUUID() + "." + split[split.length - 1];
} else {
objectName = tokenData.getFileNamePrefix() + UUID.randomUUID();
}
PutObjectRequest putObjectRequest = new PutObjectRequest(tokenData.getBucketName(), objectName, new File(filePath));
ossClient.putObject(putObjectRequest);
return objectName;
}
public ImageModerationResponse invokeFunction(String filePath) throws Exception {
//注意,此处实例化的client请尽可能重复使用,避免重复建立连接,提升检测性能。
Client client = createClient(accessKeyId, secret, endpoint);
RuntimeOptions runtime = new RuntimeOptions();
//本地文件的完整路径,例如D:\localPath\exampleFile.png。
/* String filePath = "D:\\localPath\\exampleFile.png";*/
String bucketName = null;
DescribeUploadTokenResponseBody.DescribeUploadTokenResponseBodyData uploadToken = tokenMap.get(endpoint);
//获取文件上传token
if (uploadToken == null || uploadToken.expiration <= System.currentTimeMillis() / 1000) {
DescribeUploadTokenResponse tokenResponse = client.describeUploadToken();
uploadToken = tokenResponse.getBody().getData();
bucketName = uploadToken.getBucketName();
}
//上传文件请求客户端
getOssClient(uploadToken, isVPC);
//上传文件
String objectName = uploadFile(filePath, uploadToken);
// 检测参数构造。
Map<String, String> serviceParameters = new HashMap<>();
//文件上传信息
serviceParameters.put("ossBucketName", bucketName);
serviceParameters.put("ossObjectName", objectName);
serviceParameters.put("dataId", UUID.randomUUID().toString());
ImageModerationRequest request = new ImageModerationRequest();
// 图片检测service:内容安全控制台图片增强版规则配置的serviceCode,示例:baselineCheck
// 支持service请参考:https://help.aliyun.com/document_detail/467826.html?0#p-23b-o19-gff
request.setService(imageService);
request.setServiceParameters(JSON.toJSONString(serviceParameters));
ImageModerationResponse response = null;
try {
response = client.imageModerationWithOptions(request, runtime);
} catch (Exception e) {
e.printStackTrace();
}
return response;
}
public Map greeImageScan(byte[] bytes) throws Exception {
/**
* 阿里云账号AccessKey拥有所有API的访问权限,建议您使用RAM用户进行API访问或日常运维。
* 常见获取环境变量方式:
* 方式一:
* 获取RAM用户AccessKey ID:System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
* 获取RAM用户AccessKey Secret:System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
* 方式二:
* 获取RAM用户AccessKey ID:System.getProperty("ALIBABA_CLOUD_ACCESS_KEY_ID");
* 获取RAM用户AccessKey Secret:System.getProperty("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
*/
// 接入区域和地址请根据实际情况修改。
//首先将传入的byte数组抓换为imageCache文件,再将文件路径作为参数进行文件上传
String imagePath = "imageCache";
File file = new File(imagePath);
FileOutputStream fileOutputStream = new FileOutputStream(file);
fileOutputStream.write(bytes);
ImageModerationResponse response = invokeFunction(imagePath);
Map<String, String> resultMap = new HashMap<>();
try {
// 自动路由。
if (response != null) {
//区域切换到cn-beijing。
if (500 == response.getStatusCode() || (response.getBody() != null && 500 == (response.getBody().getCode()))) {
// 接入区域和地址请根据实际情况修改。
response = invokeFunction(imagePath);
}
}
// 打印检测结果。
if (response != null) {
if (response.getStatusCode() == 200) {
ImageModerationResponseBody body = response.getBody();
System.out.println("requestId=" + body.getRequestId());
System.out.println("code=" + body.getCode());
System.out.println("msg=" + body.getMsg());
if (body.getCode() == 200) {
ImageModerationResponseBodyData data = body.getData();
System.out.println("dataId=" + data.getDataId());
List<ImageModerationResponseBodyDataResult> results = data.getResult();
if (results.get(0).getLabel().equals("nonLabel") || results.get(0).getLabel().equals("nonLabel_lib")) {
resultMap.put("suggestion", "pass");
} else {
StringBuilder stringBuilder = new StringBuilder();
for (ImageModerationResponseBodyDataResult result : results) {
System.out.println("label=" + result.getLabel());
System.out.println("confidence=" + result.getConfidence());
stringBuilder.append("[label:" + result.getLabel() + ",confidence=" + result.getConfidence() + "] ");
resultMap.put("suggestion", "block");
resultMap.put("result", stringBuilder.toString());
}
}
} else {
System.out.println("image moderation not success. code:" + body.getCode());
}
} else {
System.out.println("response not success. status:" + response.getStatusCode());
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
fileOutputStream.close();
file.delete();
}
return resultMap;
}
}
其中的accessKeyId,secret,imageService,endpoint均需要我们在yaml文件中配置或者在nacos配置中心中配置。
imageService指的是图片检测增强版支持的检测服务。有以下几种:
-
baselineCheck:通用基线检测
-
baselineCheck_pro:通用基线检测_专业版
-
baselineCheck_cb:通用基线检测_海外版
-
tonalityImprove:内容治理检测
-
aigcCheck:AIGC图片风险检测
-
aigcCheck_cb:AIGC图片风险检测_出海版
-
profilePhotoCheck:头像图片检测
-
advertisingCheck:营销素材检测
-
liveStreamCheck:视频\直播截图检测
endpoint值得是支持的地域及接入地址,如下所示,根据自己的地区选择
详细可见:如何使用图片审核增强版API_内容安全(Content Moderation)-阿里云帮助中心 (aliyun.com)
一些可以优化的地方:
因为本人比较菜,这个工具类还有很多地方需要优化,也欢迎评论区分享大家的代码。
以下是我个人认为可以优化的几个地方:
- 这个图片上传的连接最好可以重复使用,而不是上传一次就断开。
- 黑马头条提供的api的传入值类型是List<byte[]>,可以一次性处理多张图片,而我这个只能一次性处理一张,而且每处理一张就要重新建立连接,浪费资源和性能。