Updata:适配调用2.0接口

This commit is contained in:
jiaji.H 2025-10-15 17:01:31 +08:00
parent 8d3115d613
commit 62b860726d
11 changed files with 454 additions and 279 deletions

171
README.md
View File

@ -1,173 +1,4 @@
# 阿里云内容安全2.0 Demo
这是一个使用Go语言编写的阿里云内容安全2.0服务演示程序,支持对视频、图片和文字进行内容安全审核。
阿里云内容安全2.0服务demo,支持对视频、图片和文字进行内容安全审核。
## 功能特性
- 🖼️ **图片内容安全审核**
- 支持通过URL或本地文件进行图片审核
- 支持多种图片格式JPG、PNG、BMP、GIF、WEBP
- 检测场景色情、暴恐、广告、直播、Logo等
- 📝 **文本内容安全审核**
- 支持单条文本和批量文本审核
- 检测场景:反垃圾信息
- 提供详细的违规内容定位信息
- 🎥 **视频内容安全审核**
- 支持异步和同步视频审核
- 自动截帧分析
- 提供帧级别的违规内容定位
## 环境要求
- Go 1.21 或更高版本
- 阿里云账号并开通内容安全2.0服务
- 有效的阿里云AccessKey
## 安装和配置
### 1. 克隆项目
```bash
git clone <your-repo-url>
cd content-security-demo
```
### 2. 安装依赖
```bash
go mod tidy
```
### 3. 配置阿里云AccessKey
有两种方式配置AccessKey
#### 方式一:使用环境变量
```bash
export ALIBABA_CLOUD_ACCESS_KEY_ID="your_access_key_id"
export ALIBABA_CLOUD_ACCESS_KEY_SECRET="your_access_key_secret"
```
#### 方式二:使用.env文件
1. 复制配置文件:
```bash
cp env.example .env
```
2. 编辑`.env`文件填入您的AccessKey信息
```env
ALIBABA_CLOUD_ACCESS_KEY_ID=your_access_key_id_here
ALIBABA_CLOUD_ACCESS_KEY_SECRET=your_access_key_secret_here
ALIBABA_CLOUD_REGION=cn-shanghai
ALIBABA_CLOUD_ENDPOINT=green.cn-shanghai.aliyuncs.com
```
### 4. 运行程序
```bash
go run .
```
## 使用说明
运行程序后,会显示交互式菜单:
```
=== 阿里云内容安全2.0 Demo ===
支持视频、图片、文字内容安全审核
请选择要测试的功能:
1. 图片内容安全审核
2. 文本内容安全审核
3. 视频内容安全审核
4. 退出
```
### 图片审核
- **通过URL审核**输入图片的公开URL地址
- **通过文件审核**:输入本地图片文件的完整路径
支持的图片格式JPG、JPEG、PNG、BMP、GIF、WEBP
文件大小限制最大9MB
### 文本审核
- **单条文本**:输入一段文本进行审核
- **批量文本**:输入多行文本,每行一条,空行结束
### 视频审核
- **异步扫描**提交任务后立即返回任务ID可稍后查询结果
- **同步扫描**:等待审核完成并返回结果(可能需要几分钟)
## 审核结果说明
### 建议类型
- `pass`:通过
- `review`:需要人工审核
- `block`:拒绝
### 检测场景
- `porn`:色情内容
- `terrorism`:暴恐内容
- `ad`:广告内容
- `live`:直播内容
- `logo`Logo识别
- `antispam`:反垃圾信息
## 项目结构
```
.
├── main.go # 主程序入口
├── config.go # 配置管理
├── image_scanner.go # 图片审核功能
├── text_scanner.go # 文本审核功能
├── video_scanner.go # 视频审核功能
├── go.mod # Go模块文件
├── env.example # 环境变量示例
└── README.md # 项目说明
```
## 注意事项
1. **费用说明**:阿里云内容安全服务按调用次数计费,请查看[官方定价](https://www.alibabacloud.com/help/zh/content-moderation/latest/billing-overview)
2. **权限配置**建议为RAM用户创建AccessKey并授予`AliyunYundunGreenWebFullAccess`权限
3. **文件限制**
- 图片最大9MB建议分辨率大于256×256像素
- 视频:支持多种格式,建议使用异步审核
4. **网络要求**:需要能够访问阿里云服务端点
## 错误处理
程序包含完善的错误处理机制:
- 配置验证
- 网络请求错误处理
- 文件格式和大小验证
- API响应解析错误处理
## 扩展功能
可以根据需要扩展以下功能:
- 添加更多检测场景
- 实现结果缓存机制
- 添加批量处理功能
- 集成到Web服务中
## 技术支持
如有问题,请参考:
- [阿里云内容安全官方文档](https://www.alibabacloud.com/help/zh/content-moderation/)
- [Go SDK文档](https://github.com/aliyun/alibaba-cloud-sdk-go)
## 许可证
MIT License

View File

@ -25,31 +25,69 @@ func main() {
// 显示配置信息(隐藏敏感信息)
fmt.Printf("✅ 配置加载成功\n")
fmt.Printf(" 登录模式: %s\n", config.LoginMode)
fmt.Printf(" 区域: %s\n", config.Region)
fmt.Printf(" 端点: %s\n", config.Endpoint)
if config.AccessKeyID != "" {
fmt.Printf(" AccessKey ID: %s...\n", config.AccessKeyID[:8])
}
if config.AccessKeySecret != "" {
fmt.Printf(" AccessKey Secret: %s...\n", config.AccessKeySecret[:8])
if config.LoginMode == "sts" {
fmt.Printf(" RAM AccessKey ID: %s...\n", config.RAMAccessKeyID[:8])
fmt.Printf(" RAM角色ARN: %s\n", config.RAMRoleArn)
} else {
if config.AccessKeyID != "" {
fmt.Printf(" AccessKey ID: %s...\n", config.AccessKeyID[:8])
}
if config.AccessKeySecret != "" {
fmt.Printf(" AccessKey Secret: %s...\n", config.AccessKeySecret[:8])
}
}
fmt.Println()
// 检查必要的配置
if config.AccessKeyID == "" || config.AccessKeySecret == "" {
fmt.Println("❌ 缺少必要的配置信息:")
if config.AccessKeyID == "" {
fmt.Println(" - ALIBABA_CLOUD_ACCESS_KEY_ID 未设置")
if config.LoginMode == "sts" {
// STS模式检查
if config.RAMAccessKeyID == "" || config.RAMAccessKeySecret == "" || config.RAMRoleArn == "" {
fmt.Println("❌ STS模式缺少必要的配置信息:")
if config.RAMAccessKeyID == "" {
fmt.Println(" - RAM_ACCESS_KEY_ID 未设置")
}
if config.RAMAccessKeySecret == "" {
fmt.Println(" - RAM_ACCESS_KEY_SECRET 未设置")
}
if config.RAMRoleArn == "" {
fmt.Println(" - RAM_ROLE_ARN 未设置")
}
fmt.Println()
fmt.Println("请检查以下配置文件:")
fmt.Println(" - .env")
fmt.Println(" - conf/alibabacloud.env")
fmt.Println(" - 环境变量")
return
}
if config.AccessKeySecret == "" {
fmt.Println(" - ALIBABA_CLOUD_ACCESS_KEY_SECRET 未设置")
// 获取STS临时凭证
fmt.Println("正在获取STS临时凭证...")
if err := config.GetSTSToken(); err != nil {
fmt.Printf("❌ 获取STS临时凭证失败: %v\n", err)
return
}
fmt.Println("✅ STS临时凭证获取成功")
} else {
// 直接模式检查
if config.AccessKeyID == "" || config.AccessKeySecret == "" {
fmt.Println("❌ 直接模式缺少必要的配置信息:")
if config.AccessKeyID == "" {
fmt.Println(" - ALIBABA_CLOUD_ACCESS_KEY_ID 未设置")
}
if config.AccessKeySecret == "" {
fmt.Println(" - ALIBABA_CLOUD_ACCESS_KEY_SECRET 未设置")
}
fmt.Println()
fmt.Println("请检查以下配置文件:")
fmt.Println(" - .env")
fmt.Println(" - conf/alibabacloud.env")
fmt.Println(" - 环境变量")
return
}
fmt.Println()
fmt.Println("请检查以下配置文件:")
fmt.Println(" - .env")
fmt.Println(" - conf/alibabacloud.env")
fmt.Println(" - 环境变量")
return
}
// 显示菜单
@ -85,7 +123,7 @@ func showMenu() {
}
func handleImageScan(config *conf.Config) {
fmt.Println("\n=== 图片内容安全审核 ===")
fmt.Println("\n=== 图片内容安全审核2.0版本)===")
scanner, err := api.NewImageScanner(config)
if err != nil {
@ -93,7 +131,31 @@ func handleImageScan(config *conf.Config) {
return
}
fmt.Println("请选择图片输入方式:")
// 选择服务类型
fmt.Println("请选择图片审核服务类型:")
fmt.Println("1. 通用基线检测baselineCheck_global")
fmt.Println("2. 大小模型融合图片审核服务postImageCheckByVL_global")
fmt.Println("3. AI生成图片鉴别aigcDetector_global")
serviceChoice := getUserInput("请选择服务类型 (1-3): ")
var serviceType api.ImageServiceType
switch serviceChoice {
case "1":
serviceType = api.BaselineCheckGlobal
fmt.Println("✅ 已选择:通用基线检测")
case "2":
serviceType = api.PostImageCheckByVLGlobal
fmt.Println("✅ 已选择:大小模型融合图片审核服务")
case "3":
serviceType = api.AigcDetectorGlobal
fmt.Println("✅ 已选择AI生成图片鉴别")
default:
fmt.Println("❌ 无效选择")
return
}
fmt.Println("\n请选择图片输入方式:")
fmt.Println("1. 通过URL")
fmt.Println("2. 通过本地文件")
@ -108,7 +170,7 @@ func handleImageScan(config *conf.Config) {
}
fmt.Println("正在扫描图片...")
result, err := scanner.ScanImageByURL(url, "image_"+time.Now().Format("20060102150405"))
result, err := scanner.ScanImageByURL(url, "image_"+time.Now().Format("20060102150405"), serviceType)
if err != nil {
fmt.Printf("❌ 扫描失败: %v\n", err)
return
@ -123,7 +185,7 @@ func handleImageScan(config *conf.Config) {
}
fmt.Println("正在扫描图片...")
result, err := scanner.ScanImageByFile(filePath, "image_"+time.Now().Format("20060102150405"))
result, err := scanner.ScanImageByFile(filePath, "image_"+time.Now().Format("20060102150405"), serviceType)
if err != nil {
fmt.Printf("❌ 扫描失败: %v\n", err)
return
@ -136,7 +198,7 @@ func handleImageScan(config *conf.Config) {
}
func handleTextScan(config *conf.Config) {
fmt.Println("\n=== 文本内容安全审核 ===")
fmt.Println("\n=== 文本内容安全审核2.0版本)===")
scanner, err := api.NewTextScanner(config)
if err != nil {
@ -144,7 +206,27 @@ func handleTextScan(config *conf.Config) {
return
}
fmt.Println("请选择文本输入方式:")
// 选择服务类型
fmt.Println("请选择文本审核服务类型:")
fmt.Println("1. 通用基线检测baselineCheck_global")
fmt.Println("2. 大小模型融合文本审核服务postTextCheckByVL_global")
serviceChoice := getUserInput("请选择服务类型 (1-2): ")
var serviceType api.TextServiceType
switch serviceChoice {
case "1":
serviceType = api.TextBaselineCheckGlobal
fmt.Println("✅ 已选择:通用基线检测")
case "2":
serviceType = api.TextPostCheckByVLGlobal
fmt.Println("✅ 已选择:大小模型融合文本审核服务")
default:
fmt.Println("❌ 无效选择")
return
}
fmt.Println("\n请选择文本输入方式:")
fmt.Println("1. 单条文本")
fmt.Println("2. 批量文本")
@ -159,7 +241,7 @@ func handleTextScan(config *conf.Config) {
}
fmt.Println("正在扫描文本...")
result, err := scanner.ScanText(text, "text_"+time.Now().Format("20060102150405"))
result, err := scanner.ScanText(text, "text_"+time.Now().Format("20060102150405"), serviceType)
if err != nil {
fmt.Printf("❌ 扫描失败: %v\n", err)
return
@ -186,7 +268,7 @@ func handleTextScan(config *conf.Config) {
}
fmt.Printf("正在批量扫描 %d 条文本...\n", len(texts))
result, err := scanner.ScanTextBatch(texts, dataIDs)
result, err := scanner.ScanTextBatch(texts, dataIDs, serviceType)
if err != nil {
fmt.Printf("❌ 扫描失败: %v\n", err)
return
@ -199,7 +281,7 @@ func handleTextScan(config *conf.Config) {
}
func handleVideoScan(config *conf.Config) {
fmt.Println("\n=== 视频内容安全审核 ===")
fmt.Println("\n=== 视频内容安全审核2.0版本)===")
scanner, err := api.NewVideoScanner(config)
if err != nil {
@ -207,7 +289,27 @@ func handleVideoScan(config *conf.Config) {
return
}
fmt.Println("请选择视频审核方式:")
// 选择服务类型
fmt.Println("请选择视频审核服务类型:")
fmt.Println("1. 通用基线检测baselineCheck_global")
fmt.Println("2. 大小模型融合视频审核服务postVideoCheckByVL_global")
serviceChoice := getUserInput("请选择服务类型 (1-2): ")
var serviceType api.VideoServiceType
switch serviceChoice {
case "1":
serviceType = api.VideoBaselineCheckGlobal
fmt.Println("✅ 已选择:通用基线检测")
case "2":
serviceType = api.VideoPostCheckByVLGlobal
fmt.Println("✅ 已选择:大小模型融合视频审核服务")
default:
fmt.Println("❌ 无效选择")
return
}
fmt.Println("\n请选择视频审核方式:")
fmt.Println("1. 异步扫描(提交任务后立即返回)")
fmt.Println("2. 同步扫描(等待结果返回)")
@ -224,7 +326,7 @@ func handleVideoScan(config *conf.Config) {
switch choice {
case "1":
fmt.Println("正在提交视频扫描任务...")
result, err := scanner.ScanVideoAsync(videoURL, dataID)
result, err := scanner.ScanVideoAsync(videoURL, dataID, serviceType)
if err != nil {
fmt.Printf("❌ 提交任务失败: %v\n", err)
return
@ -237,7 +339,7 @@ func handleVideoScan(config *conf.Config) {
case "2":
fmt.Println("正在扫描视频(这可能需要几分钟)...")
result, err := scanner.ScanVideoAndWait(videoURL, dataID, 10*time.Minute)
result, err := scanner.ScanVideoAndWait(videoURL, dataID, serviceType, 10*time.Minute)
if err != nil {
fmt.Printf("❌ 扫描失败: %v\n", err)
return

View File

@ -1,9 +1,23 @@
#=========== 阿里云内容安全配置 ===========
# 登录模式direct直接使用AccessKey或 sts使用STS临时凭证
LOGIN_MODE=sts
# 直接登录模式配置
# 阿里云AccessKey ID
ALIBABA_CLOUD_ACCESS_KEY_ID=your_access_key_id_here
ALIBABA_CLOUD_ACCESS_KEY_ID=LTAI5tNBzbeEbG1yCitvHsMb
# 阿里云AccessKey Secret
ALIBABA_CLOUD_ACCESS_KEY_SECRET=your_access_key_secret_here
ALIBABA_CLOUD_ACCESS_KEY_SECRET=G1xAUB8G6WDVo0SLr6DJaJjNWIlpmO
# STS登录模式配置
# RAM用户AccessKey ID用于获取STS临时凭证
RAM_ACCESS_KEY_ID=LTAI5tNBzbeEbG1yCitvHsMb
# RAM用户AccessKey Secret
RAM_ACCESS_KEY_SECRET=G1xAUB8G6WDVo0SLr6DJaJjNWIlpmO
# 要扮演的RAM角色ARN
RAM_ROLE_ARN=acs:ram::5828544250383902:role/content-secret
# 阿里云区域可选默认为cn-shanghai
ALIBABA_CLOUD_REGION=cn-shanghai

View File

@ -3,17 +3,33 @@ package conf
import (
"fmt"
"os"
"strconv"
"github.com/aliyun/alibaba-cloud-sdk-go/services/sts"
"github.com/joho/godotenv"
)
// Config 配置结构体
type Config struct {
// 登录模式
LoginMode string
// 直接登录模式配置
AccessKeyID string
AccessKeySecret string
Region string
Endpoint string
// STS登录模式配置
RAMAccessKeyID string
RAMAccessKeySecret string
RAMRoleArn string
// 通用配置
Region string
Endpoint string
// STS临时凭证运行时获取
TempAccessKeyID string
TempAccessKeySecret string
SecurityToken string
}
// LoadConfig 加载配置
@ -34,10 +50,14 @@ func LoadConfig() (*Config, error) {
}
config := &Config{
AccessKeyID: getEnv("ALIBABA_CLOUD_ACCESS_KEY_ID", ""),
AccessKeySecret: getEnv("ALIBABA_CLOUD_ACCESS_KEY_SECRET", ""),
Region: getEnv("ALIBABA_CLOUD_REGION", "cn-shanghai"),
Endpoint: getEnv("ALIBABA_CLOUD_ENDPOINT", "green.cn-shanghai.aliyuncs.com"),
LoginMode: getEnv("LOGIN_MODE", "direct"),
AccessKeyID: getEnv("ALIBABA_CLOUD_ACCESS_KEY_ID", ""),
AccessKeySecret: getEnv("ALIBABA_CLOUD_ACCESS_KEY_SECRET", ""),
RAMAccessKeyID: getEnv("RAM_ACCESS_KEY_ID", ""),
RAMAccessKeySecret: getEnv("RAM_ACCESS_KEY_SECRET", ""),
RAMRoleArn: getEnv("RAM_ROLE_ARN", ""),
Region: getEnv("ALIBABA_CLOUD_REGION", "cn-shanghai"),
Endpoint: getEnv("ALIBABA_CLOUD_ENDPOINT", "green-v2.cn-shanghai.aliyuncs.com"), // 2.0版本端点
}
return config, nil
@ -53,10 +73,14 @@ func LoadConfigFromFile(configFile string) (*Config, error) {
fmt.Printf("成功加载配置文件: %s\n", configFile)
config := &Config{
AccessKeyID: getEnv("ALIBABA_CLOUD_ACCESS_KEY_ID", ""),
AccessKeySecret: getEnv("ALIBABA_CLOUD_ACCESS_KEY_SECRET", ""),
Region: getEnv("ALIBABA_CLOUD_REGION", "cn-shanghai"),
Endpoint: getEnv("ALIBABA_CLOUD_ENDPOINT", "green.cn-shanghai.aliyuncs.com"),
LoginMode: getEnv("LOGIN_MODE", "direct"),
AccessKeyID: getEnv("ALIBABA_CLOUD_ACCESS_KEY_ID", ""),
AccessKeySecret: getEnv("ALIBABA_CLOUD_ACCESS_KEY_SECRET", ""),
RAMAccessKeyID: getEnv("RAM_ACCESS_KEY_ID", ""),
RAMAccessKeySecret: getEnv("RAM_ACCESS_KEY_SECRET", ""),
RAMRoleArn: getEnv("RAM_ROLE_ARN", ""),
Region: getEnv("ALIBABA_CLOUD_REGION", "cn-shanghai"),
Endpoint: getEnv("ALIBABA_CLOUD_ENDPOINT", "green-v2.cn-shanghai.aliyuncs.com"), // 2.0版本端点
}
return config, nil
@ -70,12 +94,44 @@ func getEnv(key, defaultValue string) string {
return defaultValue
}
// getEnvAsInt 获取环境变量并转换为整数
func getEnvAsInt(key string, defaultValue int) int {
if value := os.Getenv(key); value != "" {
if intValue, err := strconv.Atoi(value); err == nil {
return intValue
}
// GetSTSToken 获取STS临时凭证
func (c *Config) GetSTSToken() error {
if c.LoginMode != "sts" {
return nil // 非STS模式不需要获取临时凭证
}
return defaultValue
// 创建STS客户端
stsClient, err := sts.NewClientWithAccessKey(c.Region, c.RAMAccessKeyID, c.RAMAccessKeySecret)
if err != nil {
return fmt.Errorf("创建STS客户端失败: %w", err)
}
// 构造AssumeRole请求
request := sts.CreateAssumeRoleRequest()
request.Scheme = "https"
request.RoleArn = c.RAMRoleArn
request.RoleSessionName = "content-security-session" // 会话名称可自定义
// 调用接口获取凭证
response, err := stsClient.AssumeRole(request)
if err != nil {
return fmt.Errorf("获取临时凭证失败: %w", err)
}
// 保存临时凭证
c.TempAccessKeyID = response.Credentials.AccessKeyId
c.TempAccessKeySecret = response.Credentials.AccessKeySecret
c.SecurityToken = response.Credentials.SecurityToken
return nil
}
// GetEffectiveCredentials 获取有效的访问凭证
func (c *Config) GetEffectiveCredentials() (accessKeyID, accessKeySecret, securityToken string) {
if c.LoginMode == "sts" && c.TempAccessKeyID != "" {
// 使用STS临时凭证
return c.TempAccessKeyID, c.TempAccessKeySecret, c.SecurityToken
}
// 使用直接凭证
return c.AccessKeyID, c.AccessKeySecret, ""
}

4
go.mod
View File

@ -3,6 +3,10 @@ module contentSecurityDemo
go 1.23
require (
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.4
github.com/alibabacloud-go/green-20220302/v2 v2.0.0
github.com/alibabacloud-go/tea v1.2.1
github.com/alibabacloud-go/tea-utils/v2 v2.0.1
github.com/aliyun/alibaba-cloud-sdk-go v1.62.706
github.com/joho/godotenv v1.5.1
)

View File

@ -15,15 +15,40 @@ import (
"github.com/aliyun/alibaba-cloud-sdk-go/services/green"
)
// ImageScanner 图片内容安全扫描器
// 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) {
client, err := green.NewClientWithAccessKey(config.Region, config.AccessKeyID, config.AccessKeySecret)
// 获取有效的访问凭证
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)
}
@ -34,13 +59,13 @@ func NewImageScanner(config *conf.Config) (*ImageScanner, error) {
}, nil
}
// ScanImageByURL 通过URL扫描图片
func (s *ImageScanner) ScanImageByURL(imageURL string, dataID string) (*model.ImageScanResponse, error) {
// 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 = "2018-05-09"
request.Version = "2022-03-02" // 内容安全2.0图片审核API版本
request.ApiName = "ImageSyncScan"
request.QueryParams["RegionId"] = s.Config.Region
@ -51,7 +76,7 @@ func (s *ImageScanner) ScanImageByURL(imageURL string, dataID string) (*model.Im
URL: imageURL,
},
},
Scenes: []string{"porn", "terrorism", "ad", "live", "logo"},
Services: []string{string(serviceType)}, // 使用服务类型而不是场景
}
requestBody, err := json.Marshal(scanRequest)
@ -74,8 +99,8 @@ func (s *ImageScanner) ScanImageByURL(imageURL string, dataID string) (*model.Im
return &scanResponse, nil
}
// ScanImageByFile 通过文件扫描图片
func (s *ImageScanner) ScanImageByFile(filePath string, dataID string) (*model.ImageScanResponse, error) {
// ScanImageByFile 通过文件扫描图片2.0版本)
func (s *ImageScanner) ScanImageByFile(filePath string, dataID string, serviceType ImageServiceType) (*model.ImageScanResponse, error) {
// 读取文件
file, err := os.Open(filePath)
if err != nil {
@ -99,9 +124,9 @@ func (s *ImageScanner) ScanImageByFile(filePath string, dataID string) (*model.I
return nil, fmt.Errorf("读取文件失败: %v", err)
}
// 检查文件类型
// 检查文件类型 - 2.0版本支持更多格式
ext := strings.ToLower(filepath.Ext(filePath))
supportedExts := []string{".jpg", ".jpeg", ".png", ".bmp", ".gif", ".webp"}
supportedExts := []string{".jpg", ".jpeg", ".png", ".bmp", ".gif", ".webp", ".tiff", ".svg", ".ico", ".heic"}
isSupported := false
for _, supportedExt := range supportedExts {
if ext == supportedExt {
@ -122,7 +147,7 @@ func (s *ImageScanner) ScanImageByFile(filePath string, dataID string) (*model.I
request.Method = "POST"
request.Scheme = "https"
request.Domain = s.Config.Endpoint
request.Version = "2018-05-09"
request.Version = "2022-03-02" // 内容安全2.0图片审核API版本
request.ApiName = "ImageSyncScan"
request.QueryParams["RegionId"] = s.Config.Region
@ -133,7 +158,7 @@ func (s *ImageScanner) ScanImageByFile(filePath string, dataID string) (*model.I
Content: base64Content,
},
},
Scenes: []string{"porn", "terrorism", "ad", "live", "logo"},
Services: []string{string(serviceType)}, // 使用服务类型而不是场景
}
requestBody, err := json.Marshal(scanRequest)
@ -156,9 +181,9 @@ func (s *ImageScanner) ScanImageByFile(filePath string, dataID string) (*model.I
return &scanResponse, nil
}
// PrintResult 打印扫描结果
// PrintResult 打印扫描结果2.0版本)
func (s *ImageScanner) PrintResult(response *model.ImageScanResponse) {
fmt.Println("=== 图片内容安全审核结果 ===")
fmt.Println("=== 图片内容安全审核结果2.0版本)===")
fmt.Printf("状态码: %d\n", response.Code)
fmt.Printf("消息: %s\n", response.Message)
@ -174,8 +199,30 @@ func (s *ImageScanner) PrintResult(response *model.ImageScanResponse) {
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("---")
}
}

View File

@ -11,15 +11,38 @@ import (
"github.com/aliyun/alibaba-cloud-sdk-go/services/green"
)
// TextScanner 文本内容安全扫描器
// TextScanner 文本内容安全扫描器2.0
type TextScanner struct {
Client *green.Client
Config *conf.Config
}
// TextServiceType 文本审核服务类型
type TextServiceType string
const (
// TextBaselineCheckGlobal 文本通用基线检测
TextBaselineCheckGlobal TextServiceType = "baselineCheck_global"
// TextPostCheckByVLGlobal 文本大小模型融合审核服务
TextPostCheckByVLGlobal TextServiceType = "postTextCheckByVL_global"
)
// NewTextScanner 创建文本扫描器
func NewTextScanner(config *conf.Config) (*TextScanner, error) {
client, err := green.NewClientWithAccessKey(config.Region, config.AccessKeyID, config.AccessKeySecret)
// 获取有效的访问凭证
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)
}
@ -30,13 +53,13 @@ func NewTextScanner(config *conf.Config) (*TextScanner, error) {
}, nil
}
// ScanText 扫描文本内容
func (s *TextScanner) ScanText(content string, dataID string) (*model.TextScanResponse, error) {
// ScanText 扫描文本内容2.0版本)
func (s *TextScanner) ScanText(content string, dataID string, serviceType TextServiceType) (*model.TextScanResponse, error) {
request := requests.NewCommonRequest()
request.Method = "POST"
request.Scheme = "https"
request.Domain = s.Config.Endpoint
request.Version = "2018-05-09"
request.Version = "2022-03-02" // 内容安全2.0文本审核API版本
request.ApiName = "TextScan"
request.QueryParams["RegionId"] = s.Config.Region
@ -47,7 +70,7 @@ func (s *TextScanner) ScanText(content string, dataID string) (*model.TextScanRe
Content: content,
},
},
Scenes: []string{"antispam"},
Services: []string{string(serviceType)}, // 使用服务类型而不是场景
}
requestBody, err := json.Marshal(scanRequest)
@ -70,8 +93,8 @@ func (s *TextScanner) ScanText(content string, dataID string) (*model.TextScanRe
return &scanResponse, nil
}
// ScanTextBatch 批量扫描文本内容
func (s *TextScanner) ScanTextBatch(texts []string, dataIDs []string) (*model.TextScanResponse, error) {
// ScanTextBatch 批量扫描文本内容2.0版本)
func (s *TextScanner) ScanTextBatch(texts []string, dataIDs []string, serviceType TextServiceType) (*model.TextScanResponse, error) {
if len(texts) != len(dataIDs) {
return nil, fmt.Errorf("文本数量和ID数量不匹配")
}
@ -80,7 +103,7 @@ func (s *TextScanner) ScanTextBatch(texts []string, dataIDs []string) (*model.Te
request.Method = "POST"
request.Scheme = "https"
request.Domain = s.Config.Endpoint
request.Version = "2018-05-09"
request.Version = "2022-03-02" // 内容安全2.0文本审核API版本
request.ApiName = "TextScan"
request.QueryParams["RegionId"] = s.Config.Region
@ -93,8 +116,8 @@ func (s *TextScanner) ScanTextBatch(texts []string, dataIDs []string) (*model.Te
}
scanRequest := model.TextScanRequest{
Tasks: tasks,
Scenes: []string{"antispam"},
Tasks: tasks,
Services: []string{string(serviceType)}, // 使用服务类型而不是场景
}
requestBody, err := json.Marshal(scanRequest)
@ -117,9 +140,9 @@ func (s *TextScanner) ScanTextBatch(texts []string, dataIDs []string) (*model.Te
return &scanResponse, nil
}
// PrintResult 打印扫描结果
// PrintResult 打印扫描结果2.0版本)
func (s *TextScanner) PrintResult(response *model.TextScanResponse) {
fmt.Println("=== 文本内容安全审核结果 ===")
fmt.Println("=== 文本内容安全审核结果2.0版本)===")
fmt.Printf("状态码: %d\n", response.Code)
fmt.Printf("消息: %s\n", response.Message)
@ -135,6 +158,9 @@ func (s *TextScanner) PrintResult(response *model.TextScanResponse) {
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)
@ -143,7 +169,17 @@ func (s *TextScanner) PrintResult(response *model.TextScanResponse) {
fmt.Println("违规详情:")
for _, detail := range result.Details {
fmt.Printf(" 内容: %s\n", detail.Context.Context)
fmt.Printf(" 位置: %v\n", detail.Context.Pos)
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("---")

View File

@ -12,15 +12,38 @@ import (
"github.com/aliyun/alibaba-cloud-sdk-go/services/green"
)
// VideoScanner 视频内容安全扫描器
// VideoScanner 视频内容安全扫描器2.0
type VideoScanner struct {
Client *green.Client
Config *conf.Config
}
// VideoServiceType 视频审核服务类型
type VideoServiceType string
const (
// VideoBaselineCheckGlobal 视频通用基线检测
VideoBaselineCheckGlobal VideoServiceType = "baselineCheck_global"
// VideoPostCheckByVLGlobal 视频大小模型融合审核服务
VideoPostCheckByVLGlobal VideoServiceType = "postVideoCheckByVL_global"
)
// NewVideoScanner 创建视频扫描器
func NewVideoScanner(config *conf.Config) (*VideoScanner, error) {
client, err := green.NewClientWithAccessKey(config.Region, config.AccessKeyID, config.AccessKeySecret)
// 获取有效的访问凭证
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)
}
@ -31,13 +54,13 @@ func NewVideoScanner(config *conf.Config) (*VideoScanner, error) {
}, nil
}
// ScanVideoAsync 异步扫描视频
func (s *VideoScanner) ScanVideoAsync(videoURL string, dataID string) (*model.VideoScanResponse, error) {
// ScanVideoAsync 异步扫描视频2.0版本)
func (s *VideoScanner) ScanVideoAsync(videoURL string, dataID string, serviceType VideoServiceType) (*model.VideoScanResponse, error) {
request := requests.NewCommonRequest()
request.Method = "POST"
request.Scheme = "https"
request.Domain = s.Config.Endpoint
request.Version = "2018-05-09"
request.Version = "2022-03-02" // 内容安全2.0视频审核API版本
request.ApiName = "VideoAsyncScan"
request.QueryParams["RegionId"] = s.Config.Region
@ -50,7 +73,7 @@ func (s *VideoScanner) ScanVideoAsync(videoURL string, dataID string) (*model.Vi
MaxFrames: 100, // 最多截取100帧
},
},
Scenes: []string{"porn", "terrorism", "ad", "live"},
Services: []string{string(serviceType)}, // 使用服务类型而不是场景
}
requestBody, err := json.Marshal(scanRequest)
@ -73,13 +96,13 @@ func (s *VideoScanner) ScanVideoAsync(videoURL string, dataID string) (*model.Vi
return &scanResponse, nil
}
// GetVideoResult 获取视频审核结果
// GetVideoResult 获取视频审核结果2.0版本)
func (s *VideoScanner) GetVideoResult(taskID string) (*model.VideoResultResponse, error) {
request := requests.NewCommonRequest()
request.Method = "POST"
request.Scheme = "https"
request.Domain = s.Config.Endpoint
request.Version = "2018-05-09"
request.Version = "2022-03-02" // 内容安全2.0视频审核API版本
request.ApiName = "VideoAsyncScanResults"
request.QueryParams["RegionId"] = s.Config.Region
@ -99,10 +122,10 @@ func (s *VideoScanner) GetVideoResult(taskID string) (*model.VideoResultResponse
return &resultResponse, nil
}
// ScanVideoAndWait 扫描视频并等待结果
func (s *VideoScanner) ScanVideoAndWait(videoURL string, dataID string, maxWaitTime time.Duration) (*model.VideoResultResponse, error) {
// ScanVideoAndWait 扫描视频并等待结果2.0版本)
func (s *VideoScanner) ScanVideoAndWait(videoURL string, dataID string, serviceType VideoServiceType, maxWaitTime time.Duration) (*model.VideoResultResponse, error) {
// 提交扫描任务
scanResponse, err := s.ScanVideoAsync(videoURL, dataID)
scanResponse, err := s.ScanVideoAsync(videoURL, dataID, serviceType)
if err != nil {
return nil, fmt.Errorf("提交扫描任务失败: %v", err)
}
@ -159,9 +182,9 @@ func (s *VideoScanner) PrintScanResult(response *model.VideoScanResponse) {
}
}
// PrintResult 打印审核结果
// PrintResult 打印审核结果2.0版本)
func (s *VideoScanner) PrintResult(response *model.VideoResultResponse) {
fmt.Println("=== 视频内容安全审核结果 ===")
fmt.Println("=== 视频内容安全审核结果2.0版本)===")
fmt.Printf("状态码: %d\n", response.Code)
fmt.Printf("消息: %s\n", response.Message)
@ -179,9 +202,31 @@ func (s *VideoScanner) PrintResult(response *model.VideoResultResponse) {
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)
}
}
// 打印帧级别的结果
if len(result.Frames) > 0 {
fmt.Println("违规帧详情:")
@ -191,6 +236,12 @@ func (s *VideoScanner) PrintResult(response *model.VideoResultResponse) {
for _, frameResult := range frame.Results {
fmt.Printf(" 场景: %s, 标签: %s, 建议: %s, 置信度: %.2f\n",
frameResult.Scene, frameResult.Label, frameResult.Suggestion, frameResult.Rate)
if frameResult.SubLabel != "" {
fmt.Printf(" 子标签: %s\n", frameResult.SubLabel)
}
if len(frameResult.Details) > 0 {
fmt.Printf(" 详细信息: %v\n", frameResult.Details)
}
}
}
}

View File

@ -1,9 +1,9 @@
package model
// ImageScanRequest 图片审核请求结构
// ImageScanRequest 图片审核请求结构2.0版本)
type ImageScanRequest struct {
Tasks []ImageTask `json:"tasks"`
Scenes []string `json:"scenes"`
Tasks []ImageTask `json:"tasks"`
Services []string `json:"services"` // 2.0版本使用services而不是scenes
}
// ImageTask 图片任务结构
@ -13,7 +13,7 @@ type ImageTask struct {
Content string `json:"content,omitempty"` // base64编码的图片内容
}
// ImageScanResponse 图片审核响应结构
// ImageScanResponse 图片审核响应结构2.0版本)
type ImageScanResponse struct {
Code int `json:"code"`
Message string `json:"message"`
@ -26,6 +26,18 @@ type ImageScanResponse struct {
Label string `json:"label"`
Suggestion string `json:"suggestion"`
Rate float64 `json:"rate"`
// 2.0版本新增字段
SubLabel string `json:"subLabel,omitempty"` // 子标签
Details []ImageDetail `json:"details,omitempty"` // 详细信息
Extras map[string]interface{} `json:"extras,omitempty"` // 扩展信息
} `json:"results"`
} `json:"data"`
}
// ImageDetail 图片审核详细信息
type ImageDetail struct {
Context struct {
Context string `json:"context"`
Pos []int `json:"pos"`
} `json:"context"`
}

View File

@ -1,9 +1,9 @@
package model
// TextScanRequest 文本审核请求结构
// TextScanRequest 文本审核请求结构2.0版本)
type TextScanRequest struct {
Tasks []TextTask `json:"tasks"`
Scenes []string `json:"scenes"`
Tasks []TextTask `json:"tasks"`
Services []string `json:"services"` // 2.0版本使用services而不是scenes
}
// TextTask 文本任务结构
@ -12,7 +12,7 @@ type TextTask struct {
Content string `json:"content"`
}
// TextScanResponse 文本审核响应结构
// TextScanResponse 文本审核响应结构2.0版本)
type TextScanResponse struct {
Code int `json:"code"`
Message string `json:"message"`
@ -25,12 +25,18 @@ type TextScanResponse struct {
Label string `json:"label"`
Suggestion string `json:"suggestion"`
Rate float64 `json:"rate"`
Details []struct {
Context struct {
Context string `json:"context"`
Pos []int `json:"pos"`
} `json:"context"`
} `json:"details"`
// 2.0版本新增字段
SubLabel string `json:"subLabel,omitempty"` // 子标签
Details []TextDetail `json:"details,omitempty"` // 详细信息
Extras map[string]interface{} `json:"extras,omitempty"` // 扩展信息
} `json:"results"`
} `json:"data"`
}
// TextDetail 文本审核详细信息
type TextDetail struct {
Context struct {
Context string `json:"context"`
Pos []int `json:"pos"`
} `json:"context"`
}

View File

@ -1,9 +1,9 @@
package model
// VideoScanRequest 视频审核请求结构
// VideoScanRequest 视频审核请求结构2.0版本)
type VideoScanRequest struct {
Tasks []VideoTask `json:"tasks"`
Scenes []string `json:"scenes"`
Tasks []VideoTask `json:"tasks"`
Services []string `json:"services"` // 2.0版本使用services而不是scenes
}
// VideoTask 视频任务结构
@ -27,7 +27,7 @@ type VideoScanResponse struct {
} `json:"data"`
}
// VideoResultResponse 视频审核结果响应结构
// VideoResultResponse 视频审核结果响应结构2.0版本)
type VideoResultResponse struct {
Code int `json:"code"`
Message string `json:"message"`
@ -42,7 +42,11 @@ type VideoResultResponse struct {
Label string `json:"label"`
Suggestion string `json:"suggestion"`
Rate float64 `json:"rate"`
Frames []struct {
// 2.0版本新增字段
SubLabel string `json:"subLabel,omitempty"` // 子标签
Details []VideoDetail `json:"details,omitempty"` // 详细信息
Extras map[string]interface{} `json:"extras,omitempty"` // 扩展信息
Frames []struct {
Offset int `json:"offset"`
URL string `json:"url"`
Results []struct {
@ -50,8 +54,20 @@ type VideoResultResponse struct {
Label string `json:"label"`
Suggestion string `json:"suggestion"`
Rate float64 `json:"rate"`
// 2.0版本新增字段
SubLabel string `json:"subLabel,omitempty"`
Details []VideoDetail `json:"details,omitempty"`
Extras map[string]interface{} `json:"extras,omitempty"`
} `json:"results"`
} `json:"frames"`
} `json:"results"`
} `json:"data"`
}
// VideoDetail 视频审核详细信息
type VideoDetail struct {
Context struct {
Context string `json:"context"`
Pos []int `json:"pos"`
} `json:"context"`
}