项目接入阿里云内容审核增强版本,文本审核与图片审核。第三方审核

为什么要引入

自己的项目新增了一个社区的服务,社区可以发布文章,浏览文章,类似一些平台的文章管理的功能。那么发布的文章肯定是需要审核的,但是又不能全部为人工审核,这样的话效率太低也忙不过来,所以就想着先引入一个自动审核,只有当自动审核不通过之后,才进行人工审核。

引入阿里云内容审核

首先介绍一下内容安全:

内容安全是识别服务,支持对图片、视频、文本、语音等对象进行多样化场景检测,有效降低内容违规风险。

目前很多平台都支持内容检测,如阿里云、腾讯云、百度AI、网易云等国内大型互联网公司都对外提供了API。

我因为已经在阿里云购买了云服务器,相对来说比较方便一些,当然阿里云提供的这个服务是收费的,但是不贵,开发测试是可以接收的。

阿里云收费标准:收费标准链接

准备

在使用内容检测API之前,需要先注册阿里云账号,然后需要创建,记住自己的accesskey和secret。

这个创建一般一个账户只能绑定两个,也是一种可以访问你的用户的一个账号密码,可以供外接平台使用,这个密码创建之后是隐藏的,难以找到,所以建议第一次创建好了之后就记录下来。

具体实现

那下面我们就来说明一下这个具体实现。

首先这个内容安全的接入分为内容安全增强版和内容安全1.0。这个区别就是内容安全1.0是需要企业认证的,也就是说个人用不了。所以你没有认证是无法调用内容安全的sdk的。

这里面注意切换版本

当然我也把文档地址给大家。大家也可以自己去看。如何调用文本检测接口进行文本内容审核_内容安全(Content Moderation)-阿里云帮助中心

下面我就来结合代码说明一下。

1.添加依赖

xml 复制代码
<!--        增强版-->
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>green20220302</artifactId>
            <version>1.1.0</version>
        </dependency>
<!--        本地-->
        <!--安装OSS SDK-->
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>3.16.3</version>
        </dependency>

2.文本审核代码工具类

注意,这里的accesskey和secret可以直接在这里写自己的,也可以在nacos或者本地进行配置,反正能读取就行了。

方法返回值为map是因为后续需要调用该方法,通过它的返回结果来判断是否需要进一步的人工审核。

Config是一些配置,用于连接到阿里云这个接口的配置。

这里为了省劲,测试的时候只用了hashmap,追求线程安全的也可以用一下concurrent包下的map类。

同时不要忘了设置set_service,这个如果自己不设置的话是不成功的。

​编辑

ini 复制代码
package com.neu.base.aliyun;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.green20220302.Client;
import com.aliyun.green20220302.models.TextModerationRequest;
import com.aliyun.green20220302.models.TextModerationResponse;
import com.aliyun.green20220302.models.TextModerationResponseBody;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.models.RuntimeOptions;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.exceptions.ServerException;
import com.aliyuncs.green.model.v20180509.TextScanRequest;
import com.aliyuncs.http.FormatType;
import com.aliyuncs.http.HttpResponse;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

import java.util.*;

@Getter
@Setter
@Component
@ConfigurationProperties(prefix = "aliyun")
public class GreenTextScan {
	
    private String accessKeyId;
    private String secret;
    //传入文本,返回一个map,包含了文本的审核结果
    public Map greenTextScanStrongVersion(String content) throws Exception {
        System.out.println(accessKeyId);
        Config config = new Config();
        System.out.println("");
        config.setAccessKeyId(accessKeyId);
        config.setAccessKeySecret(secret);
        //接入区域和地址请根据实际情况修改
        config.setRegionId("cn-shanghai");
        config.setEndpoint("green-cip.cn-shanghai.aliyuncs.com");
        //连接时超时时间,单位毫秒(ms)。
        config.setReadTimeout(6000);
        //读取时超时时间,单位毫秒(ms)。
        config.setConnectTimeout(3000);
        // 注意,此处实例化的client请尽可能重复使用,避免重复建立连接,提升检测性能
        Client client = new Client(config);

        // 创建RuntimeObject实例并设置运行参数。
        RuntimeOptions runtime = new RuntimeOptions();
        runtime.readTimeout = 10000;
        runtime.connectTimeout = 10000;

        //检测参数构造
        Map<String,Object> resultMap= new HashMap<>();
        JSONObject serviceParameters = new JSONObject();
        //设置要审核的内容
        serviceParameters.put("content", content);

        if (serviceParameters.get("content") == null || serviceParameters.getString("content").trim().length() == 0) {
            System.out.println("text moderation content is empty");
            resultMap.put("suggestion", "检测内容为空");
            return resultMap;
        }

        TextModerationRequest textModerationRequest = new TextModerationRequest();
        /*
        文本检测service:内容安全控制台文本增强版规则配置的serviceCode,示例:chat_detection
        */
        textModerationRequest.setService("comment_detection");
        textModerationRequest.setServiceParameters(serviceParameters.toJSONString());
        try {
            // 调用方法获取检测结果。
            TextModerationResponse response = client.textModerationWithOptions(textModerationRequest, runtime);

            // 自动路由。
            if (response != null) {
                // 服务端错误,区域切换到cn-beijing。
                if (500 == response.getStatusCode() || (response.getBody() != null && 500 == (response.getBody().getCode()))) {
                    // 接入区域和地址请根据实际情况修改。
                    config.setRegionId("cn-beijing");
                    config.setEndpoint("green-cip.cn-beijing.aliyuncs.com");
                    client = new Client(config);
                    response = client.textModerationWithOptions(textModerationRequest, runtime);
                }

            }
            // 打印检测结果。
            if (response != null) {
                if (response.getStatusCode() == 200) {
                    TextModerationResponseBody result = response.getBody();
                    System.out.println(JSON.toJSONString(result));
                    Integer code = result.getCode();
                    if (code != null && code == 200) {
                        TextModerationResponseBody.TextModerationResponseBodyData data = result.getData();
                        if(data.getLabels().isEmpty()&& data.getReason().isEmpty()){
                            resultMap.put("suggestion", "pass");
                            return resultMap;
                        }
                        System.out.println("labels = [" + data.getLabels() + "]");
                        System.out.println("reason = [" + data.getReason() + "]");
                        String labels=data.getLabels();
                        String reason=data.getReason();
                        resultMap.put("labels", labels);
                        resultMap.put("reason", reason);
                        resultMap.put("suggestion", "block");
                        return resultMap;

                    } else {
                        System.out.println("text moderation not success. code:" + code);
                        String information="text moderation not success :"+code;
                        resultMap.put("information", "review");
                        return resultMap;
                    }
                } else {
                    System.out.println("response not success. status:" + response.getStatusCode());
                    String information="response not success"+response.getStatusCode();
                    resultMap.put("information", "review");
                    return resultMap;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }


}
工具类测试

我们只需要把写的工具类交给springboot容器管理,然后进行注入测试就可以了。一些springboottest注解我就不写了,这里只展示测试代码。

csharp 复制代码
  @Autowired
    private GreenTextScan greenTextScan;  

/**
     * 测试文本内容审核
     */
    @Test
    public void testScanText() throws Exception {
        Map map = greenTextScan.greenTextScanStrongVersion("neu帅不帅");
        System.out.println(map.get("suggestion"));
        System.out.println(map);
    }

测试结果:

返回结果是pass的话只需要从自己的调用方法里面取出,然后进行判断就可以了。

当然你如果这么说

csharp 复制代码
  @Test
    public void testScanText() throws Exception {
        Map map = greenTextScan.greenTextScanStrongVersion("neu是不是傻子xuexiao");
        System.out.println(map.get("suggestion"));
        System.out.println(map);
    }

当然奥,我肯定热爱neu奥。

为了证明,我痛花两条测试,日子艰难,痛失几分钱,希望大家给个关注和点赞吧哈哈哈,当然这样大家也可以看看自己是否测试成功。

管理控制台 - 用量统计 (aliyun.com)

3.图片审核代码工具类

图片审核我测试过公网的图片,好像符合https协议的才可以,而我的域名是http协议的,测试一下好像即使公网可以访问但也出了问题,所以这里我采用的是本地图片测试。也就是说把我放在公网minio里的文件进行下载到本地,设置成临时文件,由于文件的存活周期所以也不用担心。

而我们实际应用当中可能一次审核多张照片,但是这个功能在内容安全1.0版本是可以实现的,但是增强版不可以,无奈我们只能多次调用该接口,下面给出工具类。

同样不要忘了设置服务类型规则的设置。

java 复制代码
public class GreenImageScan {

    private String accessKeyId;
    private String secret;

    //服务是否部署在vpc上
    public static boolean isVPC = false;

    //文件上传token endpoint->token
    public static Map<String, DescribeUploadTokenResponseBody.DescribeUploadTokenResponseBodyData> tokenMap = new HashMap<>();

    //上传文件请求客户端
    public static OSS ossClient = null;

    //内容增强扫描本地

    public Map imageScanStrongLocalVersion(String url) 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");
         */
        // 接入区域和地址请根据实际情况修改。
        ImageModerationResponse response = invokeLocalFunction(url,accessKeyId, secret, "green-cip.cn-shanghai.aliyuncs.com");
        Map<String,Object> resultMap=new HashMap<>();
        try {
            // 自动路由。
            if (response != null) {
                //区域切换到cn-beijing。
                if (500 == response.getStatusCode() || (response.getBody() != null && 500 == (response.getBody().getCode()))) {
                    // 接入区域和地址请根据实际情况修改。
                    response = invokeLocalFunction(url,accessKeyId, secret, "green-cip.cn-beijing.aliyuncs.com");
                }
            }
            // 打印检测结果。
            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());
                    resultMap.put("code",body.getCode());
                    resultMap.put("msg",body.getMsg());
                    resultMap.put("requestId",body.getRequestId());

                    if (body.getCode() == 200) {
                        ImageModerationResponseBody.ImageModerationResponseBodyData data = body.getData();
                        System.out.println("dataId=" + data.getDataId());
                        List<ImageModerationResponseBody.ImageModerationResponseBodyDataResult> results = data.getResult();
                        for (ImageModerationResponseBody.ImageModerationResponseBodyDataResult result : results) {
                            System.out.println("label=" + result.getLabel());
                            System.out.println("confidence=" + result.getConfidence());
                        }
                        resultMap.put("suggestion","pass");
                        return resultMap;
                    } else {
                        System.out.println("image moderation not success. code:" + body.getCode());
                        resultMap.put("information","image moderation not success. code:" + body.getCode());
                        resultMap.put("suggestion","review");
                        return resultMap;
                    }
                } else {
                    System.out.println("response not success. status:" + response.getStatusCode());
                    resultMap.put("information","response not success. status:" + response.getStatusCode());
                    resultMap.put("suggestion","block");
                    return resultMap;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 创建请求客户端
     *
     * @param accessKeyId
     * @param accessKeySecret
     * @param endpoint
     * @return
     * @throws Exception
     */
    public static Client createClient(String accessKeyId, String accessKeySecret, String endpoint) throws Exception {
        Config config = new Config();
        config.setAccessKeyId(accessKeyId);
        config.setAccessKeySecret(accessKeySecret);
        // 设置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 static 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 static String uploadFile(String filePath, DescribeUploadTokenResponseBody.DescribeUploadTokenResponseBodyData tokenData) throws Exception {
        //将文件路径 filePath 根据点号 . 进行分割
        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 static ImageModerationResponse invokeLocalFunction(String url,String accessKeyId, String accessKeySecret, String endpoint) throws Exception {
        //注意,此处实例化的client请尽可能重复使用,避免重复建立连接,提升检测性能。
        Client client = createClient(accessKeyId, accessKeySecret, endpoint);
        RuntimeOptions runtime = new RuntimeOptions();

        //本地文件的完整路径,例如D:\localPath\exampleFile.png。
        String filePath = url;
        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
        request.setService("baselineCheck");
        request.setServiceParameters(JSON.toJSONString(serviceParameters));

        ImageModerationResponse response = null;
        try {
            response = client.imageModerationWithOptions(request, runtime);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return response;
    }
}

这里我的思路是传入一个url,invokelocal那里的参数和imageScanlocal那里的参数传入的都是本地图片的绝对路径。

而我们这个图片拉取的时候是从minio里面拉取的,所以我们还需要从minio里下载图片,这个下载图片的方法我目前就先不给出了,额,因为我觉得不算是重点,如果大家需要的话可以评论留言,我会给出。

测试代码

这里的url我没有给出,因为图片是需要本地的,我把url下载的图片保存到本地了,这里的url是minio的访问图片。

ini 复制代码
    @Test
    public void testScanImage() throws Exception {
        String url="";
        byte[] bytes = fileStorageService.downLoadFile(url);
        //在本地创建一个临时文件,将bytes写入到临时文件中
        // 获取文件扩展名
        //文件扩展名
        String fileExtension = url.substring(url.lastIndexOf("."));
        System.out.println(fileExtension);
        Path tempFilePath ;
        try {
            tempFilePath = Files.createTempFile("temp", fileExtension);
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        // 将字节流写入本地临时文件
        try {
            Files.write(tempFilePath, bytes, StandardOpenOption.CREATE);
            System.out.println("文件下载成功,保存在:" + tempFilePath.toString());
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("文件下载失败");
        }
        String tempFileUrl = tempFilePath.toString();
        Map map = greenImageScan.imageScanStrongLocalVersion(tempFileUrl);

        System.out.println(map.get("suggestion"));
        System.out.println(map);
    }

这个就是测试结果了。又痛失我大洋了家人,给赞啊!

当然违规图片我就不给了,哈哈哈,好青年一枚奥。

相关推荐
代码小鑫1 小时前
A035-基于Spring Boot的企业内管信息化系统
java·开发语言·spring boot·后端·spring
Spy971 小时前
django 过滤器的执行
后端·python·django
camellias_6 小时前
SpringBoot(二十三)SpringBoot集成JWT
java·spring boot·后端
tebukaopu1486 小时前
springboot如何获取控制层get和Post入参
java·spring boot·后端
昔我往昔6 小时前
SpringBoot 创建对象常见的几种方式
java·spring boot·后端
灭掉c与java6 小时前
第三章springboot数据访问
java·spring boot·后端
啊松同学7 小时前
【Java】设计模式——工厂模式
java·后端·设计模式
枫叶_v7 小时前
【SpringBoot】20 同步调用、异步调用、异步回调
java·spring boot·后端
源码12159 小时前
ASP.NET MVC宠物商城系统
后端·asp.net·宠物
Ai 编码助手10 小时前
Go语言 实现将中文转化为拼音
开发语言·后端·golang