阿里云内容安全图片审核

该文章已生成可运行项目,

1.引入依赖

       <!--内容审核增强版-->
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>green20220302</artifactId>
            <version>2.20.0</version>
        </dependency>

2.配置类

@Component
@Data
@ConfigurationProperties(prefix = "aliyun.green")
public class AliyunGreenProperties {
    private String accessKeyId;
    private String accessKeySecret;
    private String endpoint;
    private String service;
}
aliyun:
  green:
    access-key-id: xxxxxxxxxx
    access-key-secret: xxxxxxxxxxx
    endpoint: green-cip.cn-shanghai.aliyuncs.com
    service: baselineCheck  # 多个服务用,分割  eg: baselineCheck,baselineCheck_pro

3.工具类


   审核工具类

package com.coame.bff.integration.aliyun.green;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.List;

@Data
public class ImageModerationResult {

    private String dataId;
    private String riskLevel;

    private final List<LabelResult> results = new ArrayList<>();
    private final List<ServiceResult> serviceResults = new ArrayList<>();

    public void addResult(String label, Float confidence) {
        results.add(new LabelResult(label, confidence));
    }

    public void addServiceResult(ServiceResult serviceResult) {
        serviceResults.add(serviceResult);
    }

    public boolean isRiskByConfidence(float threshold) {
        return results.stream().anyMatch(r -> r.confidence != null && r.confidence >= threshold)
                || serviceResults.stream()
                .flatMap(sr -> sr.getResultList().stream())
                .anyMatch(r -> r.confidence != null && r.confidence >= threshold);
    }

    public boolean isHighRisk() {
        return "high".equalsIgnoreCase(riskLevel)
                || serviceResults.stream().anyMatch(sr -> "high".equalsIgnoreCase(sr.getRiskLevel()));
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder("审核结果:\n");
        sb.append("dataId: ").append(dataId).append("\n");

        sb.append("== 通用结果 ==\n");
        for (LabelResult r : results) {
            sb.append("label: ").append(r.label).append(", confidence: ").append(r.confidence).append("\n");
        }

        sb.append("== 各服务细分结果 ==\n");
        for (ServiceResult sr : serviceResults) {
            sb.append("Service: ").append(sr.service).append(", RiskLevel: ").append(sr.riskLevel).append("\n");
            for (LabelResult lr : sr.resultList) {
                sb.append("  label: ").append(lr.label).append(", confidence: ").append(lr.confidence).append("\n");
            }
        }

        return sb.toString();
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class LabelResult {
        private String label;
        private Float confidence;
    }

    @Data
    @NoArgsConstructor
    public static class ServiceResult {
        private String service;
        private String riskLevel;
        private List<LabelResult> resultList = new ArrayList<>();

        public ServiceResult(String service, String riskLevel) {
            this.service = service;
            this.riskLevel = riskLevel;
        }

        public void addLabelResult(String label, Float confidence) {
            resultList.add(new LabelResult(label, confidence));
        }
    }
}
package com.coame.bff.integration.aliyun.green;

import com.alibaba.fastjson.JSON;
import com.aliyun.green20220302.Client;
import com.aliyun.green20220302.models.ImageBatchModerationRequest;
import com.aliyun.green20220302.models.ImageBatchModerationResponse;
import com.aliyun.green20220302.models.ImageBatchModerationResponseBody;
import com.aliyun.green20220302.models.ImageBatchModerationResponseBody.ImageBatchModerationResponseBodyData;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.models.RuntimeOptions;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

@Component
public class ImageModerationUtil {

    @Resource
    private AliyunGreenProperties properties;

    private static String accessKeyId;
    private static String accessKeySecret;
    private static String endpoint;
    private static String service;

    @PostConstruct
    public void init() {
        accessKeyId = properties.getAccessKeyId();
        accessKeySecret = properties.getAccessKeySecret();
        endpoint = properties.getEndpoint();
        service = properties.getService();
    }

    /**
     * 创建阿里云内容安全客户端
     */
    private static Client createClient() throws Exception {
        Config config = new Config();
        config.setAccessKeyId(accessKeyId);
        config.setAccessKeySecret(accessKeySecret);
        config.setEndpoint(endpoint);
        return new Client(config);
    }

    /**
     * 审核图片
     *
     * @param imageUrl 图片地址
     * @return 审核结果(完整结构)
     */
    public static ImageModerationResult moderateImage(String imageUrl) {
        try {
            Client client = createClient();
            RuntimeOptions runtime = new RuntimeOptions();

            Map<String, String> serviceParameters = new HashMap<>();
            serviceParameters.put("imageUrl", imageUrl);
            serviceParameters.put("dataId", UUID.randomUUID().toString());

            ImageBatchModerationRequest request = new ImageBatchModerationRequest();
            request.setService(service);
            request.setServiceParameters(JSON.toJSONString(serviceParameters));

            ImageBatchModerationResponse response = client.imageBatchModerationWithOptions(request, runtime);

            if (response.getStatusCode() == 200) {
                ImageBatchModerationResponseBody body = response.getBody();
                if (body.getCode() == 200) {
                    ImageBatchModerationResponseBodyData data = body.getData();
                    ImageModerationResult result = new ImageModerationResult();
                    result.setDataId(data.getDataId());
                    result.setRiskLevel(data.getRiskLevel());

                    // 通用结果 Data.Result
                    List<ImageBatchModerationResponseBody.ImageBatchModerationResponseBodyDataResult> generalResults = data.getResult();
                    if (generalResults != null) {
                        for (ImageBatchModerationResponseBody.ImageBatchModerationResponseBodyDataResult item : generalResults) {
                            result.addResult(item.getLabel(), item.getConfidence());
                        }
                    }

                    // 分服务结果 Data.Results
                    List<ImageBatchModerationResponseBody.ImageBatchModerationResponseBodyDataResults> detailedResults = data.getResults();
                    if (detailedResults != null) {
                        for (ImageBatchModerationResponseBody.ImageBatchModerationResponseBodyDataResults serviceResult : detailedResults) {
                            String service = serviceResult.getService();
                            String riskLevel = serviceResult.getRiskLevel();
                            ImageModerationResult.ServiceResult sr = new ImageModerationResult.ServiceResult(service, riskLevel);

                            List<ImageBatchModerationResponseBody.ImageBatchModerationResponseBodyDataResultsResult> labels = serviceResult.getResult();
                            if (labels != null) {
                                for (ImageBatchModerationResponseBody.ImageBatchModerationResponseBodyDataResultsResult label : labels) {
                                    sr.addLabelResult(label.getLabel(), label.getConfidence());
                                }
                            }

                            result.addServiceResult(sr);
                        }
                    }

                    return result;
                } else {
                    System.err.println("审核失败,返回code: " + body.getCode() + ",message: " + body.getMsg());
                }
            } else {
                System.err.println("请求失败,HTTP状态码: " + response.getStatusCode());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }
}

违规图片覆盖方法
 

 public static Boolean overrideWithViolationImage(String fileKey, String url) throws IOException {
        String bucketName;
        if (url.contains(OssConstants.OSS_PUBLIC_RESOURCE_CDN_URL_PREFIX)) {
            bucketName = OssConstants.OSS_PUBLIC_RESOURCE_BUCKET_NAME;
        } else if (url.contains(AliYunClientFactory.ossProperties().getCdn())) {
            bucketName = OssUtil.getBucketName();
        } else {
            log.error("未知域名,无法覆盖");
            return false;
        }

        try (InputStream splitImageStream = new URL(OssConstants.SPLIT_IMAGE_URL).openStream()) {
            OSS ossClient = new OSSClientBuilder().build(
                    OssConstants.OSS_PUBLIC_RESOURCE_ENDPOINT,
                    OssConstants.OSS_PUBLIC_RESOURCE_ACCESS_KEY_ID,
                    OssConstants.OSS_PUBLIC_RESOURCE_ACCESS_KEY_SECRET
            );
            try {
                PutObjectRequest putObjectRequest = new PutObjectRequest(
//                    OssConstants.OSS_PUBLIC_RESOURCE_BUCKET_NAME,
                        bucketName,
                        fileKey,
                        splitImageStream
                );
                PutObjectResult putObjectResult = ossClient.putObject(putObjectRequest);
                log.info("图片违规,已覆盖原路径:{} 为分裂图", fileKey);
                return true;
            } catch (Exception e) {
                log.error("覆盖违规图片为分裂图失败,原路径:{}", fileKey, e);
                return false;
            } finally {
                if (ossClient != null) {
                    ossClient.shutdown(); // 手动关闭 OSSClient
                }
            }
        }
    }

    public static String extractFileKey(String url) {
        if (StringUtils.isBlank(url)) {
            return null;
        }

        try {
            // 去除参数部分
            int queryIndex = url.indexOf("?");
            String baseUrl = queryIndex >= 0 ? url.substring(0, queryIndex) : url;
            // 提取路径部分
            URL u = new URL(baseUrl);
            String path = u.getPath(); // e.g. /dev/tenant/account/file.png or /tenant/account/file.png
            // 去除前导斜杠
            if (path.startsWith("/")) {
                path = path.substring(1);
            }
            return path;
        } catch (MalformedURLException e) {
            log.warn("URL解析失败: {}", url, e);
            return null;
        }
    }

5.Controller

package com.coame.bff.integration.controller;

import com.coame.bff.integration.aliyun.green.ImageModerationResult;
import com.coame.bff.integration.aliyun.green.ImageModerationUtil;
import com.coame.bff.integration.aliyun.oss.OssHelper;
import com.malgo.common.ErrMsgEnum;
import com.malgo.share.RpcResult;
import io.swagger.v3.oas.annotations.Operation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Slf4j
@RestController
@RequestMapping("/v1/green")
public class GreenController {

    @Operation(summary = "图片审核")
    @PostMapping("/image/review.json")
    public RpcResult<Boolean> reviewImage(@RequestBody Map<String, Object> request) {
        String url = (String) request.get("url");
        if (url == null || url.isEmpty()) {
            return RpcResult.failure(ErrMsgEnum.ParamError);
        }
        ImageModerationResult result = ImageModerationUtil.moderateImage(url);
        if (result == null) {
            return RpcResult.failure(ErrMsgEnum.SysError);
        }
        boolean isSafe = !result.isHighRisk();
        return RpcResult.success(isSafe);
    }

    @Operation(summary = "批量图片审核并处理违规图片")
    @PostMapping("/oss/batch/replaceViolationImages")
    public RpcResult<List<String>> replaceViolationImages(@RequestBody List<String> urls) {
        if (CollectionUtils.isEmpty(urls)) {
            return RpcResult.failure(ErrMsgEnum.ParamError);
        }
        List<String> violationImageUrl = new ArrayList<>(); // 违规图片的OSS路径
        for (String url : urls) {
            // 审核图片
            ImageModerationResult moderationResult = ImageModerationUtil.moderateImage(url);
            if (moderationResult == null) {
                continue;
            }
            // 判断是否违规
            boolean isHighRisk = moderationResult.isHighRisk();
            // 收集违规图片的url
            if (isHighRisk) {
                violationImageUrl.add(url);
            }
        }
        for (String url : violationImageUrl) {
            try {
                String fileKey = OssHelper.extractFileKey(url);
                if (fileKey != null) {
                    OssHelper.overrideWithViolationImage(fileKey, url);
                } else {
                    log.warn("未能提取fileKey: {}", url);
                }
            } catch (Exception e) {
                log.error("替换违规图片失败: {}", url, e);
            }
        }
        return RpcResult.success(violationImageUrl);
    }


}

本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值