阿里云图片审核集成方案
文档日期:2026年1月
目标:在图片上传流程中集成阿里云内容安全(图片审核)服务,自动识别涉黄、涉暴等违规内容
一、当前上传流程梳理
1.1 标准文件上传流程(Uploader)
1. 接收文件(IFormFile)
2. 验证文件类型(AllowFileType)
3. 生成文件路径和文件名(yyyyMMdd_xxx.ext)
4. 【当前】先上传到服务器本地
5. 【当前】从服务器本地上传到OSS
6. 【当前】OSS上传成功 → 删除本地文件
7. 【当前】OSS上传失败 → 保留本地文件
8. 返回URL(OSS成功用OSS URL,失败用本地URL)
接口位置:FileService.cs → Uploader(string type, IFormFile file)
接口路径:POST /api/File/Uploader/{type}
1.2 Base64图片上传流程(UploadBase64Image)
1. 接收Base64数据
2. 解析Base64数据(ParseBase64Data)
3. 验证图片格式(IsValidImageFormat)
4. 生成文件路径和文件名
5. 【当前】直接上传到OSS(不经过本地)
6. 返回OSS访问URL
接口位置:FileService.cs → UploadBase64Image([FromBody] Base64ImageUploadInput input)
接口路径:POST /api/File/UploadBase64Image
注意:Base64上传当前是直接上传到OSS,没有本地备份流程。
1.3 关键代码位置
| 方法 | 位置 | 行数范围 | 说明 |
|---|---|---|---|
Uploader |
FileService.cs |
101-159 | 标准文件上传主方法 |
UploadBase64Image |
FileService.cs |
1114-1209 | Base64图片上传主方法 |
UploadFileToLocalThenOSS |
FileService.cs |
540-630 | 先本地后OSS上传逻辑 |
GetOSSAccessUrl |
FileService.cs |
716-780 | 获取OSS访问URL |
二、阿里云图片审核服务说明
2.1 服务名称
- 服务名称:内容安全(Content Moderation)
- 产品名称:阿里云内容安全
- API接口:图片同步检测
/green/image/scan
2.2 检测能力
| 检测类型 | 说明 | 风险等级 |
|---|---|---|
| 涉黄 | 色情、低俗、性感等 | 高 |
| 涉暴 | 暴力、血腥、恐怖等 | 高 |
| 广告 | 二维码、广告文字等 | 中 |
| 违规文字 | OCR识别图片中的文字并审核 | 中 |
| 其他 | 政治敏感、违禁品等 | 高 |
2.3 接口信息
接口地址:https://green.cn-shanghai.aliyuncs.com/green/image/scan
请求方式:POST
Content-Type:application/json
认证方式:AccessKey签名认证(与OSS使用相同的AccessKey)
响应格式:JSON
三、集成方案设计
3.1 集成位置
最佳集成点:在保存到本地之后、上传到OSS之前进行审核
原因:
- ✅ 审核需要图片数据,本地已有文件,可直接读取
- ✅ 审核通过后再上传OSS,避免违规内容上传到OSS
- ✅ 审核不通过时,保留本地文件,不上传OSS
- ✅ 不影响现有流程,只是增加审核步骤
3.2 改造后的流程
3.2.1 标准文件上传流程(改造后)
1. 接收文件(IFormFile)
2. 验证文件类型(AllowFileType)
3. 生成文件路径和文件名(yyyyMMdd_xxx.ext)
4. 先上传到服务器本地
5. 【新增】调用阿里云图片审核接口
6. 【新增】审核不通过 → 保留本地文件,返回错误提示(不上传OSS)
7. 【新增】审核通过 → 继续流程
8. 从服务器本地上传到OSS
9. OSS上传成功 → 删除本地文件
10. OSS上传失败 → 保留本地文件
11. 返回URL(OSS成功用OSS URL,失败用本地URL)
3.2.2 Base64图片上传流程(改造后)
1. 接收Base64数据
2. 解析Base64数据(ParseBase64Data)
3. 验证图片格式(IsValidImageFormat)
4. 生成文件路径和文件名
5. 【新增】先保存Base64数据到服务器本地(临时文件)
6. 【新增】调用阿里云图片审核接口
7. 【新增】审核不通过 → 保留本地临时文件,返回错误提示(不上传OSS)
8. 【新增】审核通过 → 继续流程
9. 从服务器本地上传到OSS
10. OSS上传成功 → 删除本地临时文件
11. OSS上传失败 → 保留本地临时文件
12. 返回OSS访问URL
四、技术实现方案
4.1 创建图片审核服务类
文件位置:netcore/src/Modularity/System/NCC.System/Service/Common/ImageModerationService.cs
功能:
- 封装阿里云图片审核API调用
- 处理审核结果解析
- 统一异常处理
接口定义:
public interface IImageModerationService
{
/// <summary>
/// 图片审核(同步检测)
/// </summary>
/// <param name="imageBytes">图片字节数组</param>
/// <param name="imageUrl">图片URL(可选,如果提供URL则优先使用URL审核)</param>
/// <returns>审核结果</returns>
Task<ImageModerationResult> ScanImageAsync(byte[] imageBytes, string imageUrl = null);
/// <summary>
/// 图片审核(从本地文件路径)
/// </summary>
/// <param name="filePath">本地文件路径</param>
/// <returns>审核结果</returns>
Task<ImageModerationResult> ScanImageFromFileAsync(string filePath);
}
审核结果模型:
public class ImageModerationResult
{
/// <summary>
/// 是否通过审核
/// </summary>
public bool IsPass { get; set; }
/// <summary>
/// 审核建议(pass:通过,review:需要人工审核,block:拒绝)
/// </summary>
public string Suggestion { get; set; }
/// <summary>
/// 风险等级(normal:正常,low:低风险,medium:中风险,high:高风险)
/// </summary>
public string RiskLevel { get; set; }
/// <summary>
/// 违规类型列表
/// </summary>
public List<string> Labels { get; set; }
/// <summary>
/// 错误信息(审核失败时的错误描述)
/// </summary>
public string ErrorMessage { get; set; }
/// <summary>
/// 审核详情(JSON格式的原始响应)
/// </summary>
public string Details { get; set; }
}
4.2 配置项添加
配置文件:appsettings.json
新增配置项:
{
"NCC_App": {
"AliyunOSS": {
"AccessKeyId": "...",
"AccessKeySecret": "...",
"Endpoint": "...",
"Region": "..."
},
"ImageModeration": {
"Enabled": true,
"Endpoint": "https://green.cn-shanghai.aliyuncs.com",
"Region": "cn-shanghai",
"AccessKeyId": "", // 如果为空,使用AliyunOSS的AccessKeyId
"AccessKeySecret": "", // 如果为空,使用AliyunOSS的AccessKeySecret
"Scenes": ["porn", "terrorism", "ad", "qrcode", "live", "logo"], // 审核场景
"SuggestionLevel": "block", // 审核建议级别:pass/review/block
"RiskLevel": "high" // 风险等级阈值:normal/low/medium/high
}
}
}
配置说明:
Enabled:是否启用图片审核(可配置开关)Endpoint:内容安全服务端点(默认:https://green.cn-shanghai.aliyuncs.com)Region:服务区域(默认:cn-shanghai)AccessKeyId/AccessKeySecret:如果为空,复用OSS的AccessKeyScenes:审核场景列表SuggestionLevel:审核建议级别,block表示拒绝,review表示需要人工审核,pass表示通过RiskLevel:风险等级阈值,超过此等级视为违规
4.3 服务注册
文件位置:Startup.cs
注册代码:
#region 阿里云图片审核
var imageModerationEnabled = App.Configuration["NCC_App:ImageModeration:Enabled"] == "true";
if (imageModerationEnabled)
{
services.AddScoped<IImageModerationService, ImageModerationService>();
}
#endregion
4.4 FileService 改造
4.4.1 Uploader 方法改造
改造位置:FileService.cs → Uploader 方法
改造逻辑:
[HttpPost("Uploader/{type}")]
[AllowAnonymous]
public async Task<dynamic> Uploader(string type, IFormFile file)
{
// ... 现有代码:验证文件类型、生成路径和文件名 ...
// 先上传到本地
var (ossSuccess, localPath, ossPath) = await UploadFileToLocalThenOSS(
file,
_filePath,
ossFilePath,
_fileName,
forceStoreType);
// 【新增】图片审核逻辑
var imageModerationEnabled = _configuration["NCC_App:ImageModeration:Enabled"] == "true";
if (imageModerationEnabled && IsImageFile(fileType))
{
try
{
var moderationService = _serviceProvider.GetService<IImageModerationService>();
if (moderationService != null && !string.IsNullOrEmpty(localPath) && File.Exists(localPath))
{
var moderationResult = await moderationService.ScanImageFromFileAsync(localPath);
if (!moderationResult.IsPass)
{
// 审核不通过,保留本地文件,返回错误提示(不上传OSS)
throw NCCException.Oh($"图片审核未通过:{moderationResult.ErrorMessage ?? "图片包含违规内容"}");
}
}
}
catch (NCCException)
{
// 审核失败异常,直接抛出
throw;
}
catch (Exception ex)
{
// 审核服务异常,根据配置决定是否继续上传(降级策略)
var failOnError = _configuration["NCC_App:ImageModeration:FailOnError"] == "true";
if (failOnError)
{
throw NCCException.Oh("图片审核服务暂时不可用,请稍后重试");
}
// 否则继续上传(降级策略)
}
}
// ... 现有代码:返回URL ...
}
关键点:
- ✅ 审核在本地文件保存之后、OSS上传之前
- ✅ 审核不通过时保留本地文件,返回错误(不上传OSS)
- ✅ 审核通过后上传OSS,OSS上传成功则删除本地文件
- ✅ 审核服务异常时,可选择降级策略(继续上传或拒绝上传)
4.4.2 UploadBase64Image 方法改造
改造位置:FileService.cs → UploadBase64Image 方法
改造逻辑:
[HttpPost("UploadBase64Image")]
[AllowAnonymous]
public async Task<dynamic> UploadBase64Image([FromBody] Base64ImageUploadInput input)
{
// ... 现有代码:解析Base64、验证格式、生成路径 ...
// 【新增】先保存Base64数据到本地临时文件
string tempLocalPath = null;
try
{
var tempLocalDir = Path.Combine(FileVariable.TempFilePath, "moderation");
if (!Directory.Exists(tempLocalDir))
{
Directory.CreateDirectory(tempLocalDir);
}
tempLocalPath = Path.Combine(tempLocalDir, fileName);
await File.WriteAllBytesAsync(tempLocalPath, imageData);
// 【新增】图片审核逻辑
var imageModerationEnabled = _configuration["NCC_App:ImageModeration:Enabled"] == "true";
if (imageModerationEnabled)
{
try
{
var moderationService = _serviceProvider.GetService<IImageModerationService>();
if (moderationService != null)
{
var moderationResult = await moderationService.ScanImageFromFileAsync(tempLocalPath);
if (!moderationResult.IsPass)
{
// 审核不通过,保留临时文件,返回错误提示(不上传OSS)
throw NCCException.Oh($"图片审核未通过:{moderationResult.ErrorMessage ?? "图片包含违规内容"}");
}
}
}
catch (NCCException)
{
throw;
}
catch (Exception ex)
{
// 审核服务异常,根据配置决定是否继续上传(降级策略)
var failOnError = _configuration["NCC_App:ImageModeration:FailOnError"] == "true";
if (failOnError)
{
throw NCCException.Oh("图片审核服务暂时不可用,请稍后重试");
}
// 否则继续上传(降级策略)
}
}
// 从临时文件上传到OSS
using (var stream = new FileStream(tempLocalPath, FileMode.Open))
{
await _oSSServiceFactory.Create("aliyun").PutObjectAsync(bucketName, ossPath, stream);
}
// OSS上传成功,删除临时文件
if (File.Exists(tempLocalPath))
{
File.Delete(tempLocalPath);
}
// ... 返回URL ...
}
catch
{
// 异常时清理临时文件
if (!string.IsNullOrEmpty(tempLocalPath) && File.Exists(tempLocalPath))
{
try { File.Delete(tempLocalPath); } catch { }
}
throw;
}
}
关键点:
- ✅ Base64上传改为先保存到本地临时文件
- ✅ 审核通过后再上传到OSS,OSS上传成功则删除临时文件
- ✅ 审核不通过时保留临时文件,返回错误(不上传OSS)
4.5 辅助方法
4.5.1 IsImageFile 方法
功能:判断文件是否为图片类型
代码:
[NonAction]
private bool IsImageFile(string fileType)
{
var imageTypes = new[] { "jpg", "jpeg", "png", "gif", "bmp", "webp" };
return imageTypes.Contains(fileType.ToLower());
}
五、审核策略配置
5.1 审核建议级别(SuggestionLevel)
| 级别 | 说明 | 处理方式 |
|---|---|---|
pass |
通过 | 允许上传 |
review |
需要人工审核 | 可配置:允许上传或拒绝上传 |
block |
拒绝 | 拒绝上传,返回错误 |
配置建议:
- 严格模式:
SuggestionLevel: "block",block和review都拒绝 - 宽松模式:
SuggestionLevel: "review",仅block拒绝,review允许上传
5.2 风险等级阈值(RiskLevel)
| 等级 | 说明 | 处理方式 |
|---|---|---|
normal |
正常 | 允许上传 |
low |
低风险 | 可配置:允许上传或拒绝上传 |
medium |
中风险 | 可配置:允许上传或拒绝上传 |
high |
高风险 | 拒绝上传 |
配置建议:
- 严格模式:
RiskLevel: "high",仅high拒绝 - 中等模式:
RiskLevel: "medium",medium和high都拒绝 - 宽松模式:
RiskLevel: "low",low、medium、high都拒绝
5.3 审核场景(Scenes)
可选场景:
porn:涉黄检测terrorism:涉暴涉恐检测ad:广告检测qrcode:二维码检测live:不良场景检测logo:Logo检测
配置建议:
"Scenes": ["porn", "terrorism", "ad", "qrcode"]
六、异常处理策略
6.1 审核服务异常
场景:审核服务不可用、网络异常、API调用失败等
处理策略(可配置):
- 严格模式:审核服务异常时拒绝上传
- 宽松模式:审核服务异常时允许上传(推荐)
实现:
catch (Exception ex)
{
// 根据配置决定是否继续上传
var failOnError = _configuration["NCC_App:ImageModeration:FailOnError"] == "true";
if (failOnError)
{
throw NCCException.Oh("图片审核服务暂时不可用,请稍后重试");
}
// 否则继续上传(降级策略)
}
6.2 审核超时
场景:审核接口响应时间过长
处理策略:
- 设置超时时间(如:5秒)
- 超时后根据配置决定:继续上传或拒绝上传
实现:
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
{
try
{
var moderationResult = await moderationService.ScanImageFromFileAsync(localPath)
.WithCancellation(cts.Token);
}
catch (OperationCanceledException)
{
// 超时处理,根据配置决定是否继续上传
var failOnError = _configuration["NCC_App:ImageModeration:FailOnError"] == "true";
if (failOnError)
{
throw NCCException.Oh("图片审核超时,请稍后重试");
}
}
}
七、性能优化
7.1 异步审核
✅ 已使用 async/await,不会阻塞主线程
7.2 审核缓存(可选)
场景:相同图片重复上传
实现:
- 计算图片MD5值
- 缓存审核结果(Redis或内存缓存)
- 相同图片直接使用缓存结果
注意:需要评估缓存成本和收益
7.3 批量审核(可选)
场景:一次上传多张图片
实现:
- 使用阿里云批量审核接口
/green/image/batchscan - 减少API调用次数
注意:当前流程是单张上传,暂不需要
八、日志记录
说明:企业内部使用,暂不需要日志记录功能。
九、测试方案
9.1 功能测试
正常图片上传:
- ✅ 审核通过,正常上传到OSS
- ✅ 返回OSS URL
违规图片上传:
- ✅ 审核不通过,拒绝上传
- ✅ 返回错误提示
- ✅ 本地文件已删除
审核服务异常:
- ✅ 根据配置决定是否继续上传
9.2 性能测试
审核耗时:
- 目标:单张图片审核耗时
- 测试:上传100张图片,统计平均耗时
并发测试:
- 目标:支持10个并发上传
- 测试:同时上传10张图片
9.3 边界测试
大图片:
- 测试:上传10MB图片
- 验证:审核是否正常
小图片:
- 测试:上传1KB图片
- 验证:审核是否正常
特殊格式:
- 测试:上传WebP、GIF动图
- 验证:审核是否支持
十、配置清单
10.1 必需配置
| 配置项 | 位置 | 说明 |
|---|---|---|
ImageModeration:Enabled |
appsettings.json |
是否启用图片审核 |
ImageModeration:Endpoint |
appsettings.json |
内容安全服务端点 |
ImageModeration:Region |
appsettings.json |
服务区域 |
ImageModeration:AccessKeyId |
appsettings.json |
AccessKey ID(可选,可复用OSS) |
ImageModeration:AccessKeySecret |
appsettings.json |
AccessKey Secret(可选,可复用OSS) |
10.2 可选配置
| 配置项 | 默认值 | 说明 |
|---|---|---|
ImageModeration:Scenes |
["porn", "terrorism", "ad", "qrcode"] |
审核场景 |
ImageModeration:SuggestionLevel |
"block" |
审核建议级别 |
ImageModeration:RiskLevel |
"high" |
风险等级阈值 |
ImageModeration:FailOnError |
"false" |
审核服务异常时是否拒绝上传 |
ImageModeration:Timeout |
5 |
审核超时时间(秒) |
十一、实施步骤
11.1 第一阶段:基础集成
- ✅ 创建
ImageModerationService服务类 - ✅ 添加配置项到
appsettings.json - ✅ 在
Startup.cs注册服务 - ✅ 改造
Uploader方法,集成审核逻辑 - ✅ 测试标准文件上传流程
预计工作量:2-3人天
11.2 第二阶段:Base64上传改造
- ✅ 改造
UploadBase64Image方法 - ✅ 添加临时文件保存逻辑
- ✅ 集成审核逻辑
- ✅ 测试Base64上传流程
预计工作量:1-2人天
11.3 第三阶段:优化与完善
- ✅ 优化异常处理
- ✅ 性能测试与优化
- ✅ 文档完善
预计工作量:0.5人天
十二、风险评估
12.1 技术风险
| 风险 | 影响 | 应对措施 |
|---|---|---|
| 审核服务不可用 | 高 | 实现降级策略,允许配置是否继续上传 |
| 审核超时 | 中 | 设置超时时间,超时后根据配置决定 |
| 审核误判 | 中 | 提供人工审核机制,支持人工复查 |
| 性能影响 | 低 | 异步审核,不阻塞主流程 |
12.2 业务风险
| 风险 | 影响 | 应对措施 |
|---|---|---|
| 正常图片被误判 | 中 | 提供申诉机制,人工审核 |
| 违规图片漏检 | 高 | 定期优化审核策略,人工抽检 |
| 审核成本 | 低 | 按量计费,成本可控 |
十三、后续优化建议
13.1 人工审核机制(可选)
- 审核结果为
review时,进入人工审核队列 - 提供管理后台,支持人工审核
- 审核通过后允许上传
13.2 审核结果统计
- 统计审核通过率
- 统计违规类型分布
- 生成审核报告
13.3 白名单机制
- 支持配置白名单(特定用户或IP)
- 白名单用户跳过审核
十四、总结
14.1 集成方案要点
- ✅ 集成位置:本地保存之后、OSS上传之前
- ✅ 审核服务:创建独立的
ImageModerationService服务类 - ✅ 配置灵活:支持开关、审核级别、异常处理策略等配置
- ✅ 降级策略:审核服务异常时可选择继续上传或拒绝上传
- ✅ 文件处理:审核不通过保留本地文件,审核通过后上传OSS并删除本地文件
14.2 改造影响
- ✅ 最小化影响:仅在上传流程中增加审核步骤
- ✅ 向后兼容:可通过配置开关控制是否启用审核
- ✅ 性能可控:异步审核,不阻塞主流程
文档版本:v1.0
最后更新:2026年1月
状态:待实施