contentSecurity/internal/api/image_scanner.go
2025-10-16 15:58:12 +08:00

228 lines
6.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package api
import (
"fmt"
"io"
"os"
"path/filepath"
"strings"
"contentSecurityDemo/conf"
"contentSecurityDemo/internal/model"
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
green20220302 "github.com/alibabacloud-go/green-20220302/v2/client"
util "github.com/alibabacloud-go/tea-utils/v2/service"
"github.com/alibabacloud-go/tea/tea"
)
// ImageScanner 图片内容安全扫描器2.0
type ImageScanner struct {
Client *green20220302.Client
Config *conf.Config
}
// ImageServiceType 图片审核服务类型
type ImageServiceType string
const (
// BaselineCheckGlobal 通用基线检测
BaselineCheckGlobal ImageServiceType = "baselineCheck_global"
// PostImageCheckByVLGlobal 大小模型融合图片审核服务(专业版)
PostImageCheckByVLGlobal ImageServiceType = "postImageCheckByVL_global"
// AigcDetectorGlobal AI生成图片鉴别
AigcDetectorGlobal ImageServiceType = "aigcDetector_global"
)
// NewImageScanner 创建图片扫描器
func NewImageScanner(config *conf.Config) (*ImageScanner, error) {
// 获取有效的访问凭证
accessKeyID, accessKeySecret, securityToken := config.GetEffectiveCredentials()
// 创建配置
clientConfig := &openapi.Config{
AccessKeyId: tea.String(accessKeyID),
AccessKeySecret: tea.String(accessKeySecret),
SecurityToken: tea.String(securityToken),
RegionId: tea.String(config.Region),
Endpoint: tea.String(config.Endpoint),
}
// 创建客户端
client, err := green20220302.NewClient(clientConfig)
if err != nil {
return nil, fmt.Errorf("创建客户端失败: %v", err)
}
return &ImageScanner{
Client: client,
Config: config,
}, nil
}
// ScanImageByURL 通过URL扫描图片
func (s *ImageScanner) ScanImageByURL(imageURL string, dataID string, serviceType ImageServiceType) (*model.ImageScanResponse, error) {
// 创建运行时配置
runtime := &util.RuntimeOptions{}
runtime.ReadTimeout = tea.Int(10000)
runtime.ConnectTimeout = tea.Int(10000)
// 构建请求参数
imageScanRequest := &green20220302.ImageModerationRequest{
Service: tea.String(string(serviceType)),
ServiceParameters: tea.String(fmt.Sprintf(`{"imageUrl":"%s","dataId":"%s"}`, imageURL, dataID)),
}
// 调用API
response, err := s.Client.ImageModerationWithOptions(imageScanRequest, runtime)
if err != nil {
return nil, fmt.Errorf("请求失败: %v", err)
}
// 转换响应格式
scanResponse := &model.ImageScanResponse{
Code: tea.Int32Value(response.Body.Code),
Message: tea.StringValue(response.Body.Msg),
Data: make([]model.ImageScanData, 0),
}
// 创建单个数据项
scanData := model.ImageScanData{
DataID: *response.Body.Data.DataId,
Code: tea.Int32Value(response.Body.Code),
Message: "success",
Results: make([]*green20220302.ImageModerationResponseBodyDataResult, 0),
}
// 如果有结果数据,进行转换
if response.Body.Data != nil {
data := response.Body.Data
scanData.Results = append(scanData.Results, data.Result...)
}
scanResponse.Data = append(scanResponse.Data, scanData)
return scanResponse, nil
}
// ScanImageByFile 通过文件扫描图片
func (s *ImageScanner) ScanImageByFile(filePath string, dataID string, serviceType ImageServiceType) (*model.ImageScanResponse, error) {
// 读取文件
file, err := os.Open(filePath)
if err != nil {
return nil, fmt.Errorf("打开文件失败: %v", err)
}
defer file.Close()
// 检查文件大小限制为9MB
fileInfo, err := file.Stat()
if err != nil {
return nil, fmt.Errorf("获取文件信息失败: %v", err)
}
if fileInfo.Size() > 9*1024*1024 {
return nil, fmt.Errorf("文件大小超过9MB限制")
}
// 读取文件内容并转换为base64
fileContent, err := io.ReadAll(file)
if err != nil {
return nil, fmt.Errorf("读取文件失败: %v", err)
}
// 检查文件类型 - 2.0版本支持更多格式
ext := strings.ToLower(filepath.Ext(filePath))
supportedExts := []string{".jpg", ".jpeg", ".png", ".bmp", ".gif", ".webp", ".tiff", ".svg", ".ico", ".heic"}
isSupported := false
for _, supportedExt := range supportedExts {
if ext == supportedExt {
isSupported = true
break
}
}
if !isSupported {
return nil, fmt.Errorf("不支持的文件格式: %s", ext)
}
// 将文件内容转换为base64
base64Content := fmt.Sprintf("data:image/%s;base64,%s", ext[1:],
strings.ReplaceAll(string(fileContent), "\n", ""))
// 创建运行时配置
runtime := &util.RuntimeOptions{}
runtime.ReadTimeout = tea.Int(10000)
runtime.ConnectTimeout = tea.Int(10000)
// 构建请求参数
imageScanRequest := &green20220302.ImageModerationRequest{
Service: tea.String(string(serviceType)),
ServiceParameters: tea.String(fmt.Sprintf(`{"content":"%s"}`, base64Content)),
}
// 调用API
response, err := s.Client.ImageModerationWithOptions(imageScanRequest, runtime)
if err != nil {
return nil, fmt.Errorf("请求失败: %v", err)
}
// 转换响应格式
scanResponse := &model.ImageScanResponse{
Code: tea.Int32Value(response.Body.Code),
Message: tea.StringValue(response.Body.Msg),
Data: make([]model.ImageScanData, 0),
}
// 创建单个数据项
scanData := model.ImageScanData{
DataID: dataID,
Code: tea.Int32Value(response.Body.Code),
Results: make([]*green20220302.ImageModerationResponseBodyDataResult, 0),
}
if response.Body.Code == tea.Int32(200) {
scanData.Message = "success"
} else {
scanData.Message = tea.StringValue(response.Body.Msg)
}
// 如果有结果数据,进行转换
if response.Body.Data != nil {
// 这里需要根据实际的响应结构进行调整
// 暂时创建一个基本的结果结构
data := response.Body.Data
scanData.Results = append(scanData.Results, data.Result...)
}
scanResponse.Data = append(scanResponse.Data, scanData)
return scanResponse, nil
}
// PrintResult 打印扫描结果
func (s *ImageScanner) PrintResult(response *model.ImageScanResponse) {
fmt.Println("=== 图片内容安全审核结果===")
fmt.Printf("状态码: %d\n", response.Code)
fmt.Printf("消息: %s\n", response.Message)
for _, data := range response.Data {
fmt.Printf("\n数据ID: %s\n", data.DataID)
fmt.Printf("处理状态: %d - %s\n", data.Code, data.Message)
for _, result := range data.Results {
if result.Confidence != nil {
fmt.Printf("置信度: %f\n", *result.Confidence)
}
if result.Description != nil {
fmt.Printf("描述: %s\n", *result.Description)
}
if result.Label != nil {
fmt.Printf("标签: %s\n", *result.Label)
}
if result.RiskLevel != nil {
fmt.Printf("风险等级: %s\n", *result.RiskLevel)
}
fmt.Println("---")
}
}
}