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

为什么要引入

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

引入阿里云内容审核

首先介绍一下内容安全:

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

目前很多平台都支持内容检测,如阿里云、腾讯云、百度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);
    }

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

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

相关推荐
devlei1 小时前
从源码泄露看AI Agent未来:深度对比Claude Code原生实现与OpenClaw开源方案
android·前端·后端
努力的小郑3 小时前
Canal 不难,难的是用好:从接入到治理
后端·mysql·性能优化
Victor3564 小时前
MongoDB(87)如何使用GridFS?
后端
Victor3564 小时前
MongoDB(88)如何进行数据迁移?
后端
小红的布丁4 小时前
单线程 Redis 的高性能之道
redis·后端
GetcharZp4 小时前
Go 语言只能写后端?这款 2D 游戏引擎刷新你的认知!
后端
宁瑶琴5 小时前
COBOL语言的云计算
开发语言·后端·golang
普通网友6 小时前
阿里云国际版服务器,真的是学生党的性价比之选吗?
后端·python·阿里云·flask·云计算
IT_陈寒6 小时前
Vue的这个响应式问题,坑了我整整两小时
前端·人工智能·后端
Soofjan7 小时前
Go 内存回收-GC 源码1-触发与阶段
后端