feat:增加代码逻辑
This commit is contained in:
parent
8aee1f4641
commit
8d3115d613
34
.vscode/launch.json
vendored
Normal file
34
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
// 使用 IntelliSense 了解相关属性。
|
||||
// 悬停以查看现有属性的描述。
|
||||
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch Package",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"envFile": "${workspaceFolder}/conf/alibabacloud.env",
|
||||
"env": {
|
||||
"GOPATH":"C:\\Users\\lenovo\\go",
|
||||
"GOOS":"windows"
|
||||
},
|
||||
"program": "${workspaceFolder}/cmd",
|
||||
"args":[]
|
||||
},
|
||||
{
|
||||
"name": "Run main.go",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "debug",
|
||||
"program": "${workspaceFolder}/cmd",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"envFile": "${workspaceFolder}/conf/alibabacloud.env",
|
||||
"env": {
|
||||
"DEBUG": "true"
|
||||
},
|
||||
"dlvFlags": ["--check-go-version=false"]
|
||||
}
|
||||
]
|
||||
}
|
22
.vscode/settings.json
vendored
Normal file
22
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"workbench.colorCustomizations": {
|
||||
"activityBar.activeBackground": "#fa1b49",
|
||||
"activityBar.background": "#fa1b49",
|
||||
"activityBar.foreground": "#e7e7e7",
|
||||
"activityBar.inactiveForeground": "#e7e7e799",
|
||||
"activityBarBadge.background": "#155e02",
|
||||
"activityBarBadge.foreground": "#e7e7e7",
|
||||
"commandCenter.border": "#e7e7e799",
|
||||
"sash.hoverBorder": "#fa1b49",
|
||||
"statusBar.background": "#dd0531",
|
||||
"statusBar.foreground": "#e7e7e7",
|
||||
"statusBarItem.hoverBackground": "#fa1b49",
|
||||
"statusBarItem.remoteBackground": "#dd0531",
|
||||
"statusBarItem.remoteForeground": "#e7e7e7",
|
||||
"titleBar.activeBackground": "#dd0531",
|
||||
"titleBar.activeForeground": "#e7e7e7",
|
||||
"titleBar.inactiveBackground": "#dd053199",
|
||||
"titleBar.inactiveForeground": "#e7e7e799"
|
||||
},
|
||||
"peacock.color": "#dd0531"
|
||||
}
|
174
README.md
174
README.md
@ -1,3 +1,173 @@
|
||||
# contentSecurity
|
||||
# 阿里云内容安全2.0 Demo
|
||||
|
||||
阿里云内容安全2.0服务demo,支持对视频、图片和文字进行内容安全审核。
|
||||
这是一个使用Go语言编写的阿里云内容安全2.0服务演示程序,支持对视频、图片和文字进行内容安全审核。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- 🖼️ **图片内容安全审核**
|
||||
- 支持通过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
|
||||
|
257
cmd/main.go
Normal file
257
cmd/main.go
Normal file
@ -0,0 +1,257 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"contentSecurityDemo/conf"
|
||||
"contentSecurityDemo/internal/api"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("=== 阿里云内容安全2.0 Demo ===")
|
||||
fmt.Println("支持视频、图片、文字内容安全审核")
|
||||
fmt.Println()
|
||||
|
||||
// 加载配置
|
||||
fmt.Println("正在加载配置...")
|
||||
config, err := conf.LoadConfigFromFile("conf/alibabacloud.env")
|
||||
if err != nil {
|
||||
fmt.Printf("❌ 加载配置失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 显示配置信息(隐藏敏感信息)
|
||||
fmt.Printf("✅ 配置加载成功\n")
|
||||
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])
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
// 检查必要的配置
|
||||
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
|
||||
}
|
||||
|
||||
// 显示菜单
|
||||
for {
|
||||
showMenu()
|
||||
choice := getUserInput("请选择操作 (1-4): ")
|
||||
|
||||
switch choice {
|
||||
case "1":
|
||||
handleImageScan(config)
|
||||
case "2":
|
||||
handleTextScan(config)
|
||||
case "3":
|
||||
handleVideoScan(config)
|
||||
case "4":
|
||||
fmt.Println("感谢使用!")
|
||||
return
|
||||
default:
|
||||
fmt.Println("❌ 无效选择,请重新输入")
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
|
||||
func showMenu() {
|
||||
fmt.Println("请选择要测试的功能:")
|
||||
fmt.Println("1. 图片内容安全审核")
|
||||
fmt.Println("2. 文本内容安全审核")
|
||||
fmt.Println("3. 视频内容安全审核")
|
||||
fmt.Println("4. 退出")
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func handleImageScan(config *conf.Config) {
|
||||
fmt.Println("\n=== 图片内容安全审核 ===")
|
||||
|
||||
scanner, err := api.NewImageScanner(config)
|
||||
if err != nil {
|
||||
fmt.Printf("❌ 创建图片扫描器失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("请选择图片输入方式:")
|
||||
fmt.Println("1. 通过URL")
|
||||
fmt.Println("2. 通过本地文件")
|
||||
|
||||
choice := getUserInput("请选择 (1-2): ")
|
||||
|
||||
switch choice {
|
||||
case "1":
|
||||
url := getUserInput("请输入图片URL: ")
|
||||
if url == "" {
|
||||
fmt.Println("❌ URL不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("正在扫描图片...")
|
||||
result, err := scanner.ScanImageByURL(url, "image_"+time.Now().Format("20060102150405"))
|
||||
if err != nil {
|
||||
fmt.Printf("❌ 扫描失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
scanner.PrintResult(result)
|
||||
|
||||
case "2":
|
||||
filePath := getUserInput("请输入图片文件路径: ")
|
||||
if filePath == "" {
|
||||
fmt.Println("❌ 文件路径不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("正在扫描图片...")
|
||||
result, err := scanner.ScanImageByFile(filePath, "image_"+time.Now().Format("20060102150405"))
|
||||
if err != nil {
|
||||
fmt.Printf("❌ 扫描失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
scanner.PrintResult(result)
|
||||
|
||||
default:
|
||||
fmt.Println("❌ 无效选择")
|
||||
}
|
||||
}
|
||||
|
||||
func handleTextScan(config *conf.Config) {
|
||||
fmt.Println("\n=== 文本内容安全审核 ===")
|
||||
|
||||
scanner, err := api.NewTextScanner(config)
|
||||
if err != nil {
|
||||
fmt.Printf("❌ 创建文本扫描器失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("请选择文本输入方式:")
|
||||
fmt.Println("1. 单条文本")
|
||||
fmt.Println("2. 批量文本")
|
||||
|
||||
choice := getUserInput("请选择 (1-2): ")
|
||||
|
||||
switch choice {
|
||||
case "1":
|
||||
text := getUserInput("请输入要审核的文本: ")
|
||||
if text == "" {
|
||||
fmt.Println("❌ 文本不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("正在扫描文本...")
|
||||
result, err := scanner.ScanText(text, "text_"+time.Now().Format("20060102150405"))
|
||||
if err != nil {
|
||||
fmt.Printf("❌ 扫描失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
scanner.PrintResult(result)
|
||||
|
||||
case "2":
|
||||
fmt.Println("请输入多条文本,每行一条,输入空行结束:")
|
||||
var texts []string
|
||||
var dataIDs []string
|
||||
|
||||
for i := 1; ; i++ {
|
||||
text := getUserInput(fmt.Sprintf("文本%d: ", i))
|
||||
if text == "" {
|
||||
break
|
||||
}
|
||||
texts = append(texts, text)
|
||||
dataIDs = append(dataIDs, fmt.Sprintf("text_%d_%s", i, time.Now().Format("20060102150405")))
|
||||
}
|
||||
|
||||
if len(texts) == 0 {
|
||||
fmt.Println("❌ 没有输入任何文本")
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("正在批量扫描 %d 条文本...\n", len(texts))
|
||||
result, err := scanner.ScanTextBatch(texts, dataIDs)
|
||||
if err != nil {
|
||||
fmt.Printf("❌ 扫描失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
scanner.PrintResult(result)
|
||||
|
||||
default:
|
||||
fmt.Println("❌ 无效选择")
|
||||
}
|
||||
}
|
||||
|
||||
func handleVideoScan(config *conf.Config) {
|
||||
fmt.Println("\n=== 视频内容安全审核 ===")
|
||||
|
||||
scanner, err := api.NewVideoScanner(config)
|
||||
if err != nil {
|
||||
fmt.Printf("❌ 创建视频扫描器失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("请选择视频审核方式:")
|
||||
fmt.Println("1. 异步扫描(提交任务后立即返回)")
|
||||
fmt.Println("2. 同步扫描(等待结果返回)")
|
||||
|
||||
choice := getUserInput("请选择 (1-2): ")
|
||||
|
||||
videoURL := getUserInput("请输入视频URL: ")
|
||||
if videoURL == "" {
|
||||
fmt.Println("❌ 视频URL不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
dataID := "video_" + time.Now().Format("20060102150405")
|
||||
|
||||
switch choice {
|
||||
case "1":
|
||||
fmt.Println("正在提交视频扫描任务...")
|
||||
result, err := scanner.ScanVideoAsync(videoURL, dataID)
|
||||
if err != nil {
|
||||
fmt.Printf("❌ 提交任务失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
scanner.PrintScanResult(result)
|
||||
|
||||
if len(result.Data) > 0 && result.Data[0].TaskID != "" {
|
||||
fmt.Printf("\n💡 提示: 使用任务ID %s 可以稍后查询结果\n", result.Data[0].TaskID)
|
||||
}
|
||||
|
||||
case "2":
|
||||
fmt.Println("正在扫描视频(这可能需要几分钟)...")
|
||||
result, err := scanner.ScanVideoAndWait(videoURL, dataID, 10*time.Minute)
|
||||
if err != nil {
|
||||
fmt.Printf("❌ 扫描失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
scanner.PrintResult(result)
|
||||
|
||||
default:
|
||||
fmt.Println("❌ 无效选择")
|
||||
}
|
||||
}
|
||||
|
||||
func getUserInput(prompt string) string {
|
||||
fmt.Print(prompt)
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
input, _ := reader.ReadString('\n')
|
||||
return strings.TrimSpace(input)
|
||||
}
|
12
conf/alibabacloud.env
Normal file
12
conf/alibabacloud.env
Normal file
@ -0,0 +1,12 @@
|
||||
#=========== 阿里云内容安全配置 ===========
|
||||
# 阿里云AccessKey ID
|
||||
ALIBABA_CLOUD_ACCESS_KEY_ID=your_access_key_id_here
|
||||
|
||||
# 阿里云AccessKey Secret
|
||||
ALIBABA_CLOUD_ACCESS_KEY_SECRET=your_access_key_secret_here
|
||||
|
||||
# 阿里云区域(可选,默认为cn-shanghai)
|
||||
ALIBABA_CLOUD_REGION=cn-shanghai
|
||||
|
||||
# 阿里云端点(可选,默认为green.cn-shanghai.aliyuncs.com)
|
||||
ALIBABA_CLOUD_ENDPOINT=green.cn-shanghai.aliyuncs.com
|
81
conf/config.go
Normal file
81
conf/config.go
Normal file
@ -0,0 +1,81 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
// Config 配置结构体
|
||||
type Config struct {
|
||||
AccessKeyID string
|
||||
AccessKeySecret string
|
||||
Region string
|
||||
Endpoint string
|
||||
}
|
||||
|
||||
// LoadConfig 加载配置
|
||||
func LoadConfig() (*Config, error) {
|
||||
// 尝试加载多个可能的配置文件
|
||||
configFiles := []string{
|
||||
".env", // 根目录的.env文件
|
||||
"conf/alibabacloud.env", // 阿里云配置文件
|
||||
"alibabacloud.env", // 根目录的阿里云配置文件
|
||||
}
|
||||
|
||||
// 按顺序尝试加载配置文件
|
||||
for _, configFile := range configFiles {
|
||||
if err := godotenv.Load(configFile); err == nil {
|
||||
fmt.Printf("成功加载配置文件: %s\n", configFile)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
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"),
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// LoadConfigFromFile 从指定文件加载配置
|
||||
func LoadConfigFromFile(configFile string) (*Config, error) {
|
||||
// 加载指定的配置文件
|
||||
if err := godotenv.Load(configFile); err != nil {
|
||||
return nil, fmt.Errorf("加载配置文件失败 %s: %v", configFile, err)
|
||||
}
|
||||
|
||||
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"),
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// getEnv 获取环境变量,如果不存在则返回默认值
|
||||
func getEnv(key, defaultValue string) string {
|
||||
if value := os.Getenv(key); value != "" {
|
||||
return value
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
return defaultValue
|
||||
}
|
137
examples.md
Normal file
137
examples.md
Normal file
@ -0,0 +1,137 @@
|
||||
# 使用示例
|
||||
|
||||
## 1. 图片审核示例
|
||||
|
||||
### 通过URL审核图片
|
||||
```
|
||||
请输入图片URL: https://example.com/test-image.jpg
|
||||
```
|
||||
|
||||
### 通过本地文件审核图片
|
||||
```
|
||||
请输入图片文件路径: C:\Users\Desktop\test-image.png
|
||||
```
|
||||
|
||||
## 2. 文本审核示例
|
||||
|
||||
### 单条文本审核
|
||||
```
|
||||
请输入要审核的文本: 这是一段测试文本,包含一些敏感词汇
|
||||
```
|
||||
|
||||
### 批量文本审核
|
||||
```
|
||||
文本1: 第一条测试文本
|
||||
文本2: 第二条测试文本
|
||||
文本3: 第三条测试文本
|
||||
文本4: [空行结束输入]
|
||||
```
|
||||
|
||||
## 3. 视频审核示例
|
||||
|
||||
### 异步视频审核
|
||||
```
|
||||
请输入视频URL: https://example.com/test-video.mp4
|
||||
```
|
||||
|
||||
### 同步视频审核
|
||||
```
|
||||
请输入视频URL: https://example.com/test-video.mp4
|
||||
```
|
||||
|
||||
## 4. 审核结果示例
|
||||
|
||||
### 图片审核结果
|
||||
```
|
||||
=== 图片内容安全审核结果 ===
|
||||
状态码: 200
|
||||
消息: OK
|
||||
|
||||
数据ID: image_20231201120000
|
||||
处理状态: 200 - OK
|
||||
✅ 未检测到违规内容
|
||||
```
|
||||
|
||||
### 文本审核结果
|
||||
```
|
||||
=== 文本内容安全审核结果 ===
|
||||
状态码: 200
|
||||
消息: OK
|
||||
|
||||
数据ID: text_20231201120000
|
||||
处理状态: 200 - OK
|
||||
场景: antispam
|
||||
标签: spam
|
||||
建议: block
|
||||
置信度: 0.95
|
||||
违规详情:
|
||||
内容: 敏感词汇
|
||||
位置: [10, 15]
|
||||
---
|
||||
```
|
||||
|
||||
### 视频审核结果
|
||||
```
|
||||
=== 视频内容安全审核结果 ===
|
||||
状态码: 200
|
||||
消息: OK
|
||||
|
||||
数据ID: video_20231201120000
|
||||
任务ID: task_123456789
|
||||
处理状态: 200 - OK
|
||||
审核状态: success
|
||||
场景: porn
|
||||
标签: porn
|
||||
建议: block
|
||||
置信度: 0.88
|
||||
违规帧详情:
|
||||
时间偏移: 30秒
|
||||
帧URL: https://example.com/frame.jpg
|
||||
场景: porn, 标签: porn, 建议: block, 置信度: 0.88
|
||||
---
|
||||
```
|
||||
|
||||
## 5. 环境变量配置示例
|
||||
|
||||
### Windows (PowerShell)
|
||||
```powershell
|
||||
$env:ALIBABA_CLOUD_ACCESS_KEY_ID="your_access_key_id"
|
||||
$env:ALIBABA_CLOUD_ACCESS_KEY_SECRET="your_access_key_secret"
|
||||
```
|
||||
|
||||
### Windows (CMD)
|
||||
```cmd
|
||||
set ALIBABA_CLOUD_ACCESS_KEY_ID=your_access_key_id
|
||||
set ALIBABA_CLOUD_ACCESS_KEY_SECRET=your_access_key_secret
|
||||
```
|
||||
|
||||
### Linux/macOS
|
||||
```bash
|
||||
export ALIBABA_CLOUD_ACCESS_KEY_ID="your_access_key_id"
|
||||
export ALIBABA_CLOUD_ACCESS_KEY_SECRET="your_access_key_secret"
|
||||
```
|
||||
|
||||
### .env文件
|
||||
```env
|
||||
ALIBABA_CLOUD_ACCESS_KEY_ID=your_access_key_id
|
||||
ALIBABA_CLOUD_ACCESS_KEY_SECRET=your_access_key_secret
|
||||
ALIBABA_CLOUD_REGION=cn-shanghai
|
||||
ALIBABA_CLOUD_ENDPOINT=green.cn-shanghai.aliyuncs.com
|
||||
```
|
||||
|
||||
## 6. 常见问题
|
||||
|
||||
### Q: 如何获取阿里云AccessKey?
|
||||
A: 登录阿里云控制台,进入"访问控制" -> "用户" -> "创建AccessKey"
|
||||
|
||||
### Q: 支持哪些图片格式?
|
||||
A: 支持JPG、JPEG、PNG、BMP、GIF、WEBP格式,文件大小不超过9MB
|
||||
|
||||
### Q: 视频审核需要多长时间?
|
||||
A: 视频审核时间取决于视频长度,通常需要几分钟到十几分钟
|
||||
|
||||
### Q: 如何查看审核历史?
|
||||
A: 可以通过任务ID查询历史审核结果
|
||||
|
||||
### Q: 审核费用如何计算?
|
||||
A: 按调用次数计费,具体价格请查看阿里云官方定价页面
|
17
go.mod
Normal file
17
go.mod
Normal file
@ -0,0 +1,17 @@
|
||||
module contentSecurityDemo
|
||||
|
||||
go 1.23
|
||||
|
||||
require (
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.62.706
|
||||
github.com/joho/godotenv v1.5.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
)
|
89
go.sum
Normal file
89
go.sum
Normal file
@ -0,0 +1,89 @@
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
|
||||
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.62.706 h1:5qi9iWE+e6XoobqYHdg6rQj1o+ygAWhM4AzyvHgk4fA=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.62.706/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A=
|
||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o=
|
||||
github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
|
||||
github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg=
|
||||
github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
|
||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
|
||||
gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
|
||||
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
|
||||
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
|
||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
182
internal/api/image_scanner.go
Normal file
182
internal/api/image_scanner.go
Normal file
@ -0,0 +1,182 @@
|
||||
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 图片内容安全扫描器
|
||||
type ImageScanner struct {
|
||||
Client *green.Client
|
||||
Config *conf.Config
|
||||
}
|
||||
|
||||
// NewImageScanner 创建图片扫描器
|
||||
func NewImageScanner(config *conf.Config) (*ImageScanner, error) {
|
||||
client, err := green.NewClientWithAccessKey(config.Region, config.AccessKeyID, config.AccessKeySecret)
|
||||
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) (*model.ImageScanResponse, error) {
|
||||
request := requests.NewCommonRequest()
|
||||
request.Method = "POST"
|
||||
request.Scheme = "https"
|
||||
request.Domain = s.Config.Endpoint
|
||||
request.Version = "2018-05-09"
|
||||
request.ApiName = "ImageSyncScan"
|
||||
request.QueryParams["RegionId"] = s.Config.Region
|
||||
|
||||
scanRequest := model.ImageScanRequest{
|
||||
Tasks: []model.ImageTask{
|
||||
{
|
||||
DataID: dataID,
|
||||
URL: imageURL,
|
||||
},
|
||||
},
|
||||
Scenes: []string{"porn", "terrorism", "ad", "live", "logo"},
|
||||
}
|
||||
|
||||
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 通过文件扫描图片
|
||||
func (s *ImageScanner) ScanImageByFile(filePath string, dataID string) (*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)
|
||||
}
|
||||
|
||||
// 检查文件类型
|
||||
ext := strings.ToLower(filepath.Ext(filePath))
|
||||
supportedExts := []string{".jpg", ".jpeg", ".png", ".bmp", ".gif", ".webp"}
|
||||
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 = "2018-05-09"
|
||||
request.ApiName = "ImageSyncScan"
|
||||
request.QueryParams["RegionId"] = s.Config.Region
|
||||
|
||||
scanRequest := model.ImageScanRequest{
|
||||
Tasks: []model.ImageTask{
|
||||
{
|
||||
DataID: dataID,
|
||||
Content: base64Content,
|
||||
},
|
||||
},
|
||||
Scenes: []string{"porn", "terrorism", "ad", "live", "logo"},
|
||||
}
|
||||
|
||||
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 打印扫描结果
|
||||
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)
|
||||
|
||||
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)
|
||||
fmt.Printf("建议: %s\n", result.Suggestion)
|
||||
fmt.Printf("置信度: %.2f\n", result.Rate)
|
||||
fmt.Println("---")
|
||||
}
|
||||
}
|
||||
}
|
166
internal/api/text_scanner.go
Normal file
166
internal/api/text_scanner.go
Normal file
@ -0,0 +1,166 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"contentSecurityDemo/conf"
|
||||
"contentSecurityDemo/internal/model"
|
||||
|
||||
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
|
||||
"github.com/aliyun/alibaba-cloud-sdk-go/services/green"
|
||||
)
|
||||
|
||||
// TextScanner 文本内容安全扫描器
|
||||
type TextScanner struct {
|
||||
Client *green.Client
|
||||
Config *conf.Config
|
||||
}
|
||||
|
||||
// NewTextScanner 创建文本扫描器
|
||||
func NewTextScanner(config *conf.Config) (*TextScanner, error) {
|
||||
client, err := green.NewClientWithAccessKey(config.Region, config.AccessKeyID, config.AccessKeySecret)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建客户端失败: %v", err)
|
||||
}
|
||||
|
||||
return &TextScanner{
|
||||
Client: client,
|
||||
Config: config,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ScanText 扫描文本内容
|
||||
func (s *TextScanner) ScanText(content string, dataID string) (*model.TextScanResponse, error) {
|
||||
request := requests.NewCommonRequest()
|
||||
request.Method = "POST"
|
||||
request.Scheme = "https"
|
||||
request.Domain = s.Config.Endpoint
|
||||
request.Version = "2018-05-09"
|
||||
request.ApiName = "TextScan"
|
||||
request.QueryParams["RegionId"] = s.Config.Region
|
||||
|
||||
scanRequest := model.TextScanRequest{
|
||||
Tasks: []model.TextTask{
|
||||
{
|
||||
DataID: dataID,
|
||||
Content: content,
|
||||
},
|
||||
},
|
||||
Scenes: []string{"antispam"},
|
||||
}
|
||||
|
||||
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.TextScanResponse
|
||||
if err := json.Unmarshal(response.GetHttpContentBytes(), &scanResponse); err != nil {
|
||||
return nil, fmt.Errorf("解析响应失败: %v", err)
|
||||
}
|
||||
|
||||
return &scanResponse, nil
|
||||
}
|
||||
|
||||
// ScanTextBatch 批量扫描文本内容
|
||||
func (s *TextScanner) ScanTextBatch(texts []string, dataIDs []string) (*model.TextScanResponse, error) {
|
||||
if len(texts) != len(dataIDs) {
|
||||
return nil, fmt.Errorf("文本数量和ID数量不匹配")
|
||||
}
|
||||
|
||||
request := requests.NewCommonRequest()
|
||||
request.Method = "POST"
|
||||
request.Scheme = "https"
|
||||
request.Domain = s.Config.Endpoint
|
||||
request.Version = "2018-05-09"
|
||||
request.ApiName = "TextScan"
|
||||
request.QueryParams["RegionId"] = s.Config.Region
|
||||
|
||||
var tasks []model.TextTask
|
||||
for i, text := range texts {
|
||||
tasks = append(tasks, model.TextTask{
|
||||
DataID: dataIDs[i],
|
||||
Content: text,
|
||||
})
|
||||
}
|
||||
|
||||
scanRequest := model.TextScanRequest{
|
||||
Tasks: tasks,
|
||||
Scenes: []string{"antispam"},
|
||||
}
|
||||
|
||||
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.TextScanResponse
|
||||
if err := json.Unmarshal(response.GetHttpContentBytes(), &scanResponse); err != nil {
|
||||
return nil, fmt.Errorf("解析响应失败: %v", err)
|
||||
}
|
||||
|
||||
return &scanResponse, nil
|
||||
}
|
||||
|
||||
// PrintResult 打印扫描结果
|
||||
func (s *TextScanner) PrintResult(response *model.TextScanResponse) {
|
||||
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)
|
||||
|
||||
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)
|
||||
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)
|
||||
fmt.Printf(" 位置: %v\n", detail.Context.Pos)
|
||||
}
|
||||
}
|
||||
fmt.Println("---")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetSuggestionText 获取建议文本
|
||||
func GetSuggestionText(suggestion string) string {
|
||||
switch suggestion {
|
||||
case "pass":
|
||||
return "通过"
|
||||
case "review":
|
||||
return "需要人工审核"
|
||||
case "block":
|
||||
return "拒绝"
|
||||
default:
|
||||
return suggestion
|
||||
}
|
||||
}
|
200
internal/api/video_scanner.go
Normal file
200
internal/api/video_scanner.go
Normal file
@ -0,0 +1,200 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"contentSecurityDemo/conf"
|
||||
"contentSecurityDemo/internal/model"
|
||||
|
||||
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
|
||||
"github.com/aliyun/alibaba-cloud-sdk-go/services/green"
|
||||
)
|
||||
|
||||
// VideoScanner 视频内容安全扫描器
|
||||
type VideoScanner struct {
|
||||
Client *green.Client
|
||||
Config *conf.Config
|
||||
}
|
||||
|
||||
// NewVideoScanner 创建视频扫描器
|
||||
func NewVideoScanner(config *conf.Config) (*VideoScanner, error) {
|
||||
client, err := green.NewClientWithAccessKey(config.Region, config.AccessKeyID, config.AccessKeySecret)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建客户端失败: %v", err)
|
||||
}
|
||||
|
||||
return &VideoScanner{
|
||||
Client: client,
|
||||
Config: config,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ScanVideoAsync 异步扫描视频
|
||||
func (s *VideoScanner) ScanVideoAsync(videoURL string, dataID string) (*model.VideoScanResponse, error) {
|
||||
request := requests.NewCommonRequest()
|
||||
request.Method = "POST"
|
||||
request.Scheme = "https"
|
||||
request.Domain = s.Config.Endpoint
|
||||
request.Version = "2018-05-09"
|
||||
request.ApiName = "VideoAsyncScan"
|
||||
request.QueryParams["RegionId"] = s.Config.Region
|
||||
|
||||
scanRequest := model.VideoScanRequest{
|
||||
Tasks: []model.VideoTask{
|
||||
{
|
||||
DataID: dataID,
|
||||
URL: videoURL,
|
||||
Interval: 1, // 每秒截取一帧
|
||||
MaxFrames: 100, // 最多截取100帧
|
||||
},
|
||||
},
|
||||
Scenes: []string{"porn", "terrorism", "ad", "live"},
|
||||
}
|
||||
|
||||
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.VideoScanResponse
|
||||
if err := json.Unmarshal(response.GetHttpContentBytes(), &scanResponse); err != nil {
|
||||
return nil, fmt.Errorf("解析响应失败: %v", err)
|
||||
}
|
||||
|
||||
return &scanResponse, nil
|
||||
}
|
||||
|
||||
// GetVideoResult 获取视频审核结果
|
||||
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.ApiName = "VideoAsyncScanResults"
|
||||
request.QueryParams["RegionId"] = s.Config.Region
|
||||
|
||||
requestBody := fmt.Sprintf(`{"taskId": "%s"}`, taskID)
|
||||
request.Content = []byte(requestBody)
|
||||
|
||||
response, err := s.Client.ProcessCommonRequest(request)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("请求失败: %v", err)
|
||||
}
|
||||
|
||||
var resultResponse model.VideoResultResponse
|
||||
if err := json.Unmarshal(response.GetHttpContentBytes(), &resultResponse); err != nil {
|
||||
return nil, fmt.Errorf("解析响应失败: %v", err)
|
||||
}
|
||||
|
||||
return &resultResponse, nil
|
||||
}
|
||||
|
||||
// ScanVideoAndWait 扫描视频并等待结果
|
||||
func (s *VideoScanner) ScanVideoAndWait(videoURL string, dataID string, maxWaitTime time.Duration) (*model.VideoResultResponse, error) {
|
||||
// 提交扫描任务
|
||||
scanResponse, err := s.ScanVideoAsync(videoURL, dataID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("提交扫描任务失败: %v", err)
|
||||
}
|
||||
|
||||
if len(scanResponse.Data) == 0 {
|
||||
return nil, fmt.Errorf("未获取到任务ID")
|
||||
}
|
||||
|
||||
taskID := scanResponse.Data[0].TaskID
|
||||
fmt.Printf("视频扫描任务已提交,任务ID: %s\n", taskID)
|
||||
|
||||
// 轮询获取结果
|
||||
startTime := time.Now()
|
||||
for time.Since(startTime) < maxWaitTime {
|
||||
result, err := s.GetVideoResult(taskID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取结果失败: %v", err)
|
||||
}
|
||||
|
||||
if len(result.Data) > 0 {
|
||||
status := result.Data[0].Status
|
||||
fmt.Printf("当前状态: %s\n", status)
|
||||
|
||||
switch status {
|
||||
case "success":
|
||||
return result, nil
|
||||
case "failure":
|
||||
return nil, fmt.Errorf("视频审核失败: %s", result.Data[0].Message)
|
||||
case "running", "pending":
|
||||
// 继续等待
|
||||
time.Sleep(5 * time.Second)
|
||||
default:
|
||||
return nil, fmt.Errorf("未知状态: %s", status)
|
||||
}
|
||||
} else {
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("等待超时,请稍后手动查询结果,任务ID: %s", taskID)
|
||||
}
|
||||
|
||||
// PrintScanResult 打印扫描提交结果
|
||||
func (s *VideoScanner) PrintScanResult(response *model.VideoScanResponse) {
|
||||
fmt.Println("=== 视频扫描任务提交结果 ===")
|
||||
fmt.Printf("状态码: %d\n", response.Code)
|
||||
fmt.Printf("消息: %s\n", response.Message)
|
||||
|
||||
for _, data := range response.Data {
|
||||
fmt.Printf("数据ID: %s\n", data.DataID)
|
||||
fmt.Printf("任务ID: %s\n", data.TaskID)
|
||||
fmt.Printf("处理状态: %d - %s\n", data.Code, data.Message)
|
||||
fmt.Printf("视频URL: %s\n", data.URL)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintResult 打印审核结果
|
||||
func (s *VideoScanner) PrintResult(response *model.VideoResultResponse) {
|
||||
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("任务ID: %s\n", data.TaskID)
|
||||
fmt.Printf("处理状态: %d - %s\n", data.Code, data.Message)
|
||||
fmt.Printf("审核状态: %s\n", data.Status)
|
||||
|
||||
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)
|
||||
fmt.Printf("建议: %s\n", result.Suggestion)
|
||||
fmt.Printf("置信度: %.2f\n", result.Rate)
|
||||
|
||||
// 打印帧级别的结果
|
||||
if len(result.Frames) > 0 {
|
||||
fmt.Println("违规帧详情:")
|
||||
for _, frame := range result.Frames {
|
||||
fmt.Printf(" 时间偏移: %d秒\n", frame.Offset)
|
||||
fmt.Printf(" 帧URL: %s\n", frame.URL)
|
||||
for _, frameResult := range frame.Results {
|
||||
fmt.Printf(" 场景: %s, 标签: %s, 建议: %s, 置信度: %.2f\n",
|
||||
frameResult.Scene, frameResult.Label, frameResult.Suggestion, frameResult.Rate)
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Println("---")
|
||||
}
|
||||
}
|
||||
}
|
31
internal/model/image.go
Normal file
31
internal/model/image.go
Normal file
@ -0,0 +1,31 @@
|
||||
package model
|
||||
|
||||
// ImageScanRequest 图片审核请求结构
|
||||
type ImageScanRequest struct {
|
||||
Tasks []ImageTask `json:"tasks"`
|
||||
Scenes []string `json:"scenes"`
|
||||
}
|
||||
|
||||
// ImageTask 图片任务结构
|
||||
type ImageTask struct {
|
||||
DataID string `json:"dataId"`
|
||||
URL string `json:"url,omitempty"`
|
||||
Content string `json:"content,omitempty"` // base64编码的图片内容
|
||||
}
|
||||
|
||||
// ImageScanResponse 图片审核响应结构
|
||||
type ImageScanResponse struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data []struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
DataID string `json:"dataId"`
|
||||
Results []struct {
|
||||
Scene string `json:"scene"`
|
||||
Label string `json:"label"`
|
||||
Suggestion string `json:"suggestion"`
|
||||
Rate float64 `json:"rate"`
|
||||
} `json:"results"`
|
||||
} `json:"data"`
|
||||
}
|
36
internal/model/text.go
Normal file
36
internal/model/text.go
Normal file
@ -0,0 +1,36 @@
|
||||
package model
|
||||
|
||||
// TextScanRequest 文本审核请求结构
|
||||
type TextScanRequest struct {
|
||||
Tasks []TextTask `json:"tasks"`
|
||||
Scenes []string `json:"scenes"`
|
||||
}
|
||||
|
||||
// TextTask 文本任务结构
|
||||
type TextTask struct {
|
||||
DataID string `json:"dataId"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
// TextScanResponse 文本审核响应结构
|
||||
type TextScanResponse struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data []struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
DataID string `json:"dataId"`
|
||||
Results []struct {
|
||||
Scene string `json:"scene"`
|
||||
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"`
|
||||
} `json:"results"`
|
||||
} `json:"data"`
|
||||
}
|
57
internal/model/video.go
Normal file
57
internal/model/video.go
Normal file
@ -0,0 +1,57 @@
|
||||
package model
|
||||
|
||||
// VideoScanRequest 视频审核请求结构
|
||||
type VideoScanRequest struct {
|
||||
Tasks []VideoTask `json:"tasks"`
|
||||
Scenes []string `json:"scenes"`
|
||||
}
|
||||
|
||||
// VideoTask 视频任务结构
|
||||
type VideoTask struct {
|
||||
DataID string `json:"dataId"`
|
||||
URL string `json:"url"`
|
||||
Interval int `json:"interval,omitempty"` // 截帧间隔,单位秒
|
||||
MaxFrames int `json:"maxFrames,omitempty"` // 最大截帧数
|
||||
}
|
||||
|
||||
// VideoScanResponse 视频审核响应结构
|
||||
type VideoScanResponse struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data []struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
DataID string `json:"dataId"`
|
||||
TaskID string `json:"taskId"`
|
||||
URL string `json:"url"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// VideoResultResponse 视频审核结果响应结构
|
||||
type VideoResultResponse struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data []struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
DataID string `json:"dataId"`
|
||||
TaskID string `json:"taskId"`
|
||||
Status string `json:"status"`
|
||||
Results []struct {
|
||||
Scene string `json:"scene"`
|
||||
Label string `json:"label"`
|
||||
Suggestion string `json:"suggestion"`
|
||||
Rate float64 `json:"rate"`
|
||||
Frames []struct {
|
||||
Offset int `json:"offset"`
|
||||
URL string `json:"url"`
|
||||
Results []struct {
|
||||
Scene string `json:"scene"`
|
||||
Label string `json:"label"`
|
||||
Suggestion string `json:"suggestion"`
|
||||
Rate float64 `json:"rate"`
|
||||
} `json:"results"`
|
||||
} `json:"frames"`
|
||||
} `json:"results"`
|
||||
} `json:"data"`
|
||||
}
|
44
run.bat
Normal file
44
run.bat
Normal file
@ -0,0 +1,44 @@
|
||||
@echo off
|
||||
echo === 阿里云内容安全2.0 Demo ===
|
||||
echo.
|
||||
|
||||
REM 检查Go是否安装
|
||||
go version >nul 2>&1
|
||||
if errorlevel 1 (
|
||||
echo ❌ 错误: 未找到Go,请先安装Go 1.21或更高版本
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo ✅ Go环境检查通过
|
||||
echo.
|
||||
|
||||
REM 下载依赖
|
||||
echo 正在下载依赖...
|
||||
go mod tidy
|
||||
if errorlevel 1 (
|
||||
echo ❌ 依赖下载失败
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo ✅ 依赖下载完成
|
||||
echo.
|
||||
|
||||
REM 检查环境变量
|
||||
if "%ALIBABA_CLOUD_ACCESS_KEY_ID%"=="" (
|
||||
echo ⚠️ 警告: 未设置 ALIBABA_CLOUD_ACCESS_KEY_ID 环境变量
|
||||
echo 请设置环境变量或创建.env文件
|
||||
echo.
|
||||
)
|
||||
|
||||
if "%ALIBABA_CLOUD_ACCESS_KEY_SECRET%"=="" (
|
||||
echo ⚠️ 警告: 未设置 ALIBABA_CLOUD_ACCESS_KEY_SECRET 环境变量
|
||||
echo 请设置环境变量或创建.env文件
|
||||
echo.
|
||||
)
|
||||
|
||||
REM 运行程序
|
||||
echo 启动程序...
|
||||
go run cmd/main.go
|
||||
pause
|
41
run.sh
Normal file
41
run.sh
Normal file
@ -0,0 +1,41 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "=== 阿里云内容安全2.0 Demo ==="
|
||||
echo
|
||||
|
||||
# 检查Go是否安装
|
||||
if ! command -v go &> /dev/null; then
|
||||
echo "❌ 错误: 未找到Go,请先安装Go 1.21或更高版本"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Go环境检查通过"
|
||||
echo
|
||||
|
||||
# 下载依赖
|
||||
echo "正在下载依赖..."
|
||||
go mod tidy
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "❌ 依赖下载失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ 依赖下载完成"
|
||||
echo
|
||||
|
||||
# 检查环境变量
|
||||
if [ -z "$ALIBABA_CLOUD_ACCESS_KEY_ID" ]; then
|
||||
echo "⚠️ 警告: 未设置 ALIBABA_CLOUD_ACCESS_KEY_ID 环境变量"
|
||||
echo "请设置环境变量或创建.env文件"
|
||||
echo
|
||||
fi
|
||||
|
||||
if [ -z "$ALIBABA_CLOUD_ACCESS_KEY_SECRET" ]; then
|
||||
echo "⚠️ 警告: 未设置 ALIBABA_CLOUD_ACCESS_KEY_SECRET 环境变量"
|
||||
echo "请设置环境变量或创建.env文件"
|
||||
echo
|
||||
fi
|
||||
|
||||
# 运行程序
|
||||
echo "启动程序..."
|
||||
go run cmd/main.go
|
Loading…
Reference in New Issue
Block a user