contentSecurity/internal/api/image_scanner.go
2025-10-15 17:01:31 +08:00

230 lines
6.3 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 (
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"contentSecurityDemo/conf"
"contentSecurityDemo/internal/model"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
"github.com/aliyun/alibaba-cloud-sdk-go/services/green"
)
// ImageScanner 图片内容安全扫描器2.0
type ImageScanner struct {
Client *green.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()
var client *green.Client
var err error
if securityToken != "" {
// 使用STS临时凭证
client, err = green.NewClientWithStsToken(config.Region, accessKeyID, accessKeySecret, securityToken)
} else {
// 使用直接凭证
client, err = green.NewClientWithAccessKey(config.Region, accessKeyID, accessKeySecret)
}
if err != nil {
return nil, fmt.Errorf("创建客户端失败: %v", err)
}
return &ImageScanner{
Client: client,
Config: config,
}, nil
}
// ScanImageByURL 通过URL扫描图片2.0版本)
func (s *ImageScanner) ScanImageByURL(imageURL string, dataID string, serviceType ImageServiceType) (*model.ImageScanResponse, error) {
request := requests.NewCommonRequest()
request.Method = "POST"
request.Scheme = "https"
request.Domain = s.Config.Endpoint
request.Version = "2022-03-02" // 内容安全2.0图片审核API版本
request.ApiName = "ImageSyncScan"
request.QueryParams["RegionId"] = s.Config.Region
scanRequest := model.ImageScanRequest{
Tasks: []model.ImageTask{
{
DataID: dataID,
URL: imageURL,
},
},
Services: []string{string(serviceType)}, // 使用服务类型而不是场景
}
requestBody, err := json.Marshal(scanRequest)
if err != nil {
return nil, fmt.Errorf("序列化请求失败: %v", err)
}
request.Content = requestBody
response, err := s.Client.ProcessCommonRequest(request)
if err != nil {
return nil, fmt.Errorf("请求失败: %v", err)
}
var scanResponse model.ImageScanResponse
if err := json.Unmarshal(response.GetHttpContentBytes(), &scanResponse); err != nil {
return nil, fmt.Errorf("解析响应失败: %v", err)
}
return &scanResponse, nil
}
// ScanImageByFile 通过文件扫描图片2.0版本)
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", ""))
request := requests.NewCommonRequest()
request.Method = "POST"
request.Scheme = "https"
request.Domain = s.Config.Endpoint
request.Version = "2022-03-02" // 内容安全2.0图片审核API版本
request.ApiName = "ImageSyncScan"
request.QueryParams["RegionId"] = s.Config.Region
scanRequest := model.ImageScanRequest{
Tasks: []model.ImageTask{
{
DataID: dataID,
Content: base64Content,
},
},
Services: []string{string(serviceType)}, // 使用服务类型而不是场景
}
requestBody, err := json.Marshal(scanRequest)
if err != nil {
return nil, fmt.Errorf("序列化请求失败: %v", err)
}
request.Content = requestBody
response, err := s.Client.ProcessCommonRequest(request)
if err != nil {
return nil, fmt.Errorf("请求失败: %v", err)
}
var scanResponse model.ImageScanResponse
if err := json.Unmarshal(response.GetHttpContentBytes(), &scanResponse); err != nil {
return nil, fmt.Errorf("解析响应失败: %v", err)
}
return &scanResponse, nil
}
// PrintResult 打印扫描结果2.0版本)
func (s *ImageScanner) PrintResult(response *model.ImageScanResponse) {
fmt.Println("=== 图片内容安全审核结果2.0版本)===")
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)
if len(data.Results) == 0 {
fmt.Println("✅ 未检测到违规内容")
continue
}
for _, result := range data.Results {
fmt.Printf("场景: %s\n", result.Scene)
fmt.Printf("标签: %s\n", result.Label)
if result.SubLabel != "" {
fmt.Printf("子标签: %s\n", result.SubLabel)
}
fmt.Printf("建议: %s\n", result.Suggestion)
fmt.Printf("置信度: %.2f\n", result.Rate)
// 打印详细信息
if len(result.Details) > 0 {
fmt.Println("详细信息:")
for _, detail := range result.Details {
fmt.Printf(" 内容: %s\n", detail.Context.Context)
if len(detail.Context.Pos) > 0 {
fmt.Printf(" 位置: %v\n", detail.Context.Pos)
}
}
}
// 打印扩展信息
if len(result.Extras) > 0 {
fmt.Println("扩展信息:")
for key, value := range result.Extras {
fmt.Printf(" %s: %v\n", key, value)
}
}
fmt.Println("---")
}
}
}