PHP 图片审核接入

wen PHP项目 2

本文目录导读:

PHP 图片审核接入

  1. 常见图片审核服务商
  2. 通用图片审核封装类
  3. 本地图片审核实现
  4. 完整接入示例
  5. 配置和优化建议
  6. 注意事项

我将为您介绍PHP图片审核的接入方案,主要涵盖常见的云服务商API对接和实现方案。

常见图片审核服务商

1 阿里云内容安全

<?php
class AliyunImageReview {
    private $accessKeyId;
    private $accessKeySecret;
    public function __construct($accessKeyId, $accessKeySecret) {
        $this->accessKeyId = $accessKeyId;
        $this->accessKeySecret = $accessKeySecret;
    }
    /**
     * 图片审核
     * @param string $imageUrl 图片URL
     * @return array 审核结果
     */
    public function reviewImage($imageUrl) {
        $host = "https://green.cn-shanghai.aliyuncs.com";
        $path = "/green/image/scan";
        $content = json_encode([
            'tasks' => [
                ['dataId' => uniqid(), 'url' => $imageUrl]
            ],
            'scenes' => ['porn', 'terrorism', 'politician']
        ], JSON_UNESCAPED_UNICODE);
        // 生成签名
        $time = time() * 1000;
        $nonce = md5(uniqid());
        // 使用阿里云SDK生成签名
        $signature = $this->generateSignature($content, $time, $nonce);
        $headers = [
            'Authorization: ' . $signature,
            'Content-Type: application/json',
            'x-acs-signature-method: HMAC-SHA1',
            'x-acs-signature-nonce: ' . $nonce,
            'x-acs-signature-version: 1.0',
            'x-acs-version: 2018-05-09',
            'Date: ' . gmdate('D, d M Y H:i:s \G\M\T', time())
        ];
        // 发送请求
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $host . $path);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $content);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        $response = curl_exec($ch);
        curl_close($ch);
        return json_decode($response, true);
    }
    private function generateSignature($content, $time, $nonce) {
        // 签名字符串构造
        $stringToSign = $content . $time . $nonce;
        $signature = base64_encode(
            hash_hmac('sha1', $stringToSign, $this->accessKeySecret, true)
        );
        return "acs " . $this->accessKeyId . ":" . $signature;
    }
}

2 腾讯云图片审核

<?php
class TencentImageReview {
    private $secretId;
    private $secretKey;
    public function __construct($secretId, $secretKey) {
        $this->secretId = $secretId;
        $this->secretKey = $secretKey;
    }
    /**
     * 图片审核
     * @param string $imageUrl 图片URL
     * @return array 审核结果
     */
    public function reviewImage($imageUrl) {
        // 使用腾讯云SDK
        try {
            $credential = new Credential($this->secretId, $this->secretKey);
            $client = new ImageModerationClient($credential, "ap-guangzhou");
            $req = new ImageModerationRequest();
            $params = json_encode([
                'ImageUrl' => $imageUrl,
                'BizType' => ''
            ]);
            $req->fromJsonString($params);
            $resp = $client->ImageModeration($req);
            return json_decode($resp->toJsonString(), true);
        } catch (Exception $e) {
            return ['error' => $e->getMessage()];
        }
    }
}

通用图片审核封装类

<?php
class ImageReviewService {
    private $provider;
    private $config;
    public function __construct($provider = 'aliyun') {
        $this->provider = $provider;
        $this->config = $this->loadConfig();
    }
    private function loadConfig() {
        return [
            'aliyun' => [
                'accessKeyId' => getenv('ALIYUN_ACCESS_KEY_ID'),
                'accessKeySecret' => getenv('ALIYUN_ACCESS_KEY_SECRET'),
                'region' => 'cn-shanghai'
            ],
            'tencent' => [
                'secretId' => getenv('TENCENT_SECRET_ID'),
                'secretKey' => getenv('TENCENT_SECRET_KEY'),
                'region' => 'ap-guangzhou'
            ]
        ];
    }
    /**
     * 统一审核入口
     * @param string $imageUrl
     * @param string $type 审核类型:image/video
     * @return array
     */
    public function review($imageUrl, $type = 'image') {
        try {
            switch ($this->provider) {
                case 'aliyun':
                    $result = $this->reviewWithAliyun($imageUrl);
                    break;
                case 'tencent':
                    $result = $this->reviewWithTencent($imageUrl);
                    break;
                default:
                    throw new Exception("不支持的审核服务商");
            }
            return $this->formatResult($result);
        } catch (Exception $e) {
            return [
                'success' => false,
                'error' => $e->getMessage(),
                'suggestion' => 'block'
            ];
        }
    }
    /**
     * 统一结果格式
     */
    private function formatResult($result) {
        $default = [
            'success' => false,
            'suggestion' => 'pass', // pass, review, block
            'labels' => [],
            'score' => 0,
            'raw' => $result
        ];
        // 根据不同服务商解析结果
        if ($this->provider == 'aliyun') {
            return $this->parseAliyunResult($result, $default);
        } elseif ($this->provider == 'tencent') {
            return $this->parseTencentResult($result, $default);
        }
        return $default;
    }
    private function parseAliyunResult($result, $default) {
        if (isset($result['data'][0]['results'][0])) {
            $review = $result['data'][0]['results'][0];
            $default['suggestion'] = $review['suggestion'];
            $default['score'] = $review['rate'] ?? 0;
            foreach ($review['details'] ?? [] as $detail) {
                $default['labels'][] = [
                    'label' => $detail['label'],
                    'score' => $detail['rate'] ?? 0
                ];
            }
            $default['success'] = true;
        }
        return $default;
    }
    private function parseTencentResult($result, $default) {
        if (isset($result['Data']['Suggestion'])) {
            $default['suggestion'] = $result['Data']['Suggestion'];
            if (isset($result['Data']['Label'])) {
                $default['labels'][] = [
                    'label' => $result['Data']['Label'],
                    'score' => $result['Data']['Score'] ?? 0
                ];
            }
            $default['score'] = $result['Data']['Score'] ?? 0;
            $default['success'] = true;
        }
        return $default;
    }
    private function reviewWithAliyun($imageUrl) {
        // 阿里云审核实现
        $aliyun = new AliyunImageReview(
            $this->config['aliyun']['accessKeyId'],
            $this->config['aliyun']['accessKeySecret']
        );
        return $aliyun->reviewImage($imageUrl);
    }
    private function reviewWithTencent($imageUrl) {
        // 腾讯云审核实现
        $tencent = new TencentImageReview(
            $this->config['tencent']['secretId'],
            $this->config['tencent']['secretKey']
        );
        return $tencent->reviewImage($imageUrl);
    }
}

本地图片审核实现

<?php
class LocalImageReview {
    /**
     * 基于规则的本地图片审核
     */
    public function reviewLocalImage($imagePath) {
        $result = [
            'success' => false,
            'suggestion' => 'pass',
            'reason' => ''
        ];
        try {
            // 1. 基础检查
            if (!file_exists($imagePath)) {
                throw new Exception("图片不存在");
            }
            // 2. 获取图片信息
            $imageInfo = getimagesize($imagePath);
            $width = $imageInfo[0];
            $height = $imageInfo[1];
            $mime = $imageInfo['mime'];
            // 3. 检查图片大小
            $fileSize = filesize($imagePath);
            $maxSize = 10 * 1024 * 1024; // 10MB
            if ($fileSize > $maxSize) {
                $result['suggestion'] = 'block';
                $result['reason'] = '图片过大';
                return $result;
            }
            // 4. 检查图片格式
            $allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
            if (!in_array($mime, $allowedTypes)) {
                $result['suggestion'] = 'block';
                $result['reason'] = '不支持的图片格式';
                return $result;
            }
            // 5. 基于GD库的简单检测
            if (extension_loaded('gd')) {
                $image = imagecreatefromstring(file_get_contents($imagePath));
                // 检测肤色比例(简单的色情内容检测)
                $skinRatio = $this->detectSkinRatio($image);
                if ($skinRatio > 0.4) {
                    $result['suggestion'] = 'review';
                    $result['reason'] = '图片可能包含不当内容';
                }
                imagedestroy($image);
            }
            $result['success'] = true;
        } catch (Exception $e) {
            $result['success'] = false;
            $result['reason'] = $e->getMessage();
        }
        return $result;
    }
    /**
     * 简单的肤色检测(仅作示例)
     */
    private function detectSkinRatio($image) {
        $width = imagesx($image);
        $height = imagesy($image);
        $skinCount = 0;
        $totalCount = 0;
        for ($x = 0; $x < $width; $x += 5) {
            for ($y = 0; $y < $height; $y += 5) {
                $rgb = imagecolorat($image, $x, $y);
                $r = ($rgb >> 16) & 0xFF;
                $g = ($rgb >> 8) & 0xFF;
                $b = $rgb & 0xFF;
                // 简单的肤色判断逻辑
                if ($r > 95 && $g > 40 && $b > 20 && 
                    $r > $g && $r > $b && 
                    $r - $gamax > 15 && 
                    $r - $b > 15) {
                    $skinCount++;
                }
                $totalCount++;
            }
        }
        return $skinCount / max($totalCount, 1);
    }
    /**
     * 批量审核图片
     */
    public function batchReview($imagePaths) {
        $results = [];
        foreach ($imagePaths as $imagePath) {
            $results[$imagePath] = $this->reviewLocalImage($imagePath);
        }
        return $results;
    }
}

完整接入示例

<?php
// 接口调用示例
require_once 'ImageReviewService.php';
require_once 'LocalImageReview.php';
class ImageReviewController {
    private $reviewService;
    private $localReviewer;
    public function __construct() {
        // 使用阿里云服务
        $this->reviewService = new ImageReviewService('aliyun');
        $this->localReviewer = new LocalImageReview();
    }
    /**
     * 上传并审核图片
     */
    public function uploadAndReview() {
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
            return $this->jsonResponse(false, '请求方法不正确');
        }
        if (!isset($_FILES['image'])) {
            return $this->jsonResponse(false, '没有上传图片');
        }
        $file = $_FILES['image'];
        // 1. 保存临时文件
        $uploadDir = '/tmp/uploads/';
        if (!is_dir($uploadDir)) {
            mkdir($uploadDir, 0755, true);
        }
        $tempFile = $uploadDir . uniqid() . '_' . $file['name'];
        if (!move_uploaded_file($file['tmp_name'], $tempFile)) {
            return $this->jsonResponse(false, '文件保存失败');
        }
        // 2. 本地基础审核
        $localResult = $this->localReviewer->reviewLocalImage($tempFile);
        if ($localResult['suggestion'] === 'block') {
            return $this->jsonResponse(false, '图片审核未通过:' . $localResult['reason']);
        }
        // 3. 上传到对象存储或获取可访问URL
        $imageUrl = $this->uploadToStorage($tempFile);
        // 4. 云端专业审核
        $cloudResult = $this->reviewService->review($imageUrl);
        // 5. 处理审核结果
        $response = [
            'success' => true,
            'data' => [
                'imageUrl' => $imageUrl,
                'reviewResult' => $cloudResult
            ]
        ];
        if ($cloudResult['suggestion'] === 'block') {
            $response['data']['status'] = 'blocked';
            $response['data']['message'] = '图片包含违规内容';
        } elseif ($cloudResult['suggestion'] === 'review') {
            $response['data']['status'] = 'pending';
            $response['data']['message'] = '图片需要人工审核';
        } else {
            $response['data']['status'] = 'approved';
            $response['data']['message'] = '图片审核通过';
        }
        // 清理临时文件
        unlink($tempFile);
        return $this->jsonResponse(true, '审核完成', $response['data']);
    }
    /**
     * 上传到对象存储(示例)
     */
    private function uploadToStorage($filePath) {
        // 这里实现你的对象存储上传逻辑
        // OSS、COS、七牛云等
        $ossUrl = 'https://your-bucket.oss-cn-shanghai.aliyuncs.com/' . basename($filePath);
        // 简化示例,实际需要上传文件
        return $ossUrl;
    }
    /**
     * JSON响应
     */
    private function jsonResponse($success, $message, $data = []) {
        header('Content-Type: application/json');
        echo json_encode([
            'success' => $success,
            'message' => $message,
            'data' => $data
        ], JSON_UNESCAPED_UNICODE);
        exit;
    }
}
// 使用示例
$controller = new ImageReviewController();
$controller->uploadAndReview();

配置和优化建议

<?php
// 配置文件 config.php
return [
    // 审核服务配置
    'review' => [
        'provider' => 'aliyun',  // 服务商:aliyun/tencent
        // 阿里云配置
        'aliyun' => [
            'accessKeyId' => 'your-access-key-id',
            'accessKeySecret' => 'your-access-key-secret',
            'region' => 'cn-shanghai',
            'scenes' => ['porn', 'terrorism', 'politician', 'ad']
        ],
        // 腾讯云配置
        'tencent' => [
            'secretId' => 'your-secret-id',
            'secretKey' => 'your-secret-key',
            'region' => 'ap-guangzhou'
        ],
        // 审核策略
        'strategy' => [
            'allowPass' => true,       // 是否允许通过
            'allowReview' => true,     // 是否需要人工审核
            'maxBlockScore' => 80,     // 拦截分数
            'minPassScore' => 20       // 通过分数
        ]
    ],
    // 图片上传配置
    'upload' => [
        'maxSize' => 10 * 1024 * 1024, // 10MB
        'allowedTypes' => ['jpg', 'jpeg', 'png', 'gif', 'webp'],
        'storage' => 'oss',            // oss/cos/qiniu/local
        'oss' => [
            'bucket' => 'your-bucket',
            'endpoint' => 'oss-cn-shanghai.aliyuncs.com'
        ]
    ]
];

注意事项

  1. 安全性:API密钥务必通过环境变量或配置文件管理,不要硬编码在代码中
  2. 缓存优化:对审核结果进行缓存,避免重复审核
  3. 异步处理:大批量图片审核建议使用队列异步处理
  4. 兜底方案:云服务异常时,确保有本地审核方案兜底
  5. 合规性:确保图片审核流程符合相关法律法规要求

方案可根据实际需求选择使用,建议结合本地预审和云服务精审,在保证效果的同时控制成本。

抱歉,评论功能暂时关闭!