回调准备工作
确定回调 URL
要开始设置回调服务,请首先确定您要接收回调的 URL。这是您的系统将接收来自我们服务器的 HTTP POST 请求的地址。确保您的URL是公共可访问的,并支持 HTTPS 协议。
配置回调参数
在您的系统中,您需要配置一些参数以接收和处理我们发送的 HTTP POST 请求。以下是一些重要的参数:
请求方法:HTTP POST。
请求头:Content-Type: application / json。
请求体:JSON 格式的数据,包含有关回调结果的详细信息,详情信息请参考接收回调结果。
您需要根据您的系统需求调整这些参数。请确保您的系统能够解析和处理 JSON 格式的请求体。
测试回调服务
在实际使用回调服务之前,请确保您已经成功地设置了回调 URL 和参数,并且您的系统已经可以接收和处理来自我们服务器的 HTTP POST 请求。您可以使用一些在线工具来模拟 HTTP POST 请求,以确保您的回调服务正常工作。
处理回调事件
当我们的服务器发送 HTTP POST 请求到您的回调 URL 时,您的系统需要解析请求体中的 JSON 数据,并根据事件状态 Status 执行相应的操作。以下是一些常见的事件类型:
FINISH:任务审核成功。
RUNNING:任务还在执行中。
ERROR:任务执行失败,可参考回调中的失败类型(ErrorType)和具体原因(ErrorDescription)字段来确认失败原因。
请根据您的系统需求来处理不同的事件类型。请确保响应的 HTTP 状态码为200 OK。
接收回调结果
注意:
在点播场景中,默认情况下只有当审核结束(Finish)时才会触发回调。而在直播场景中,默认情况下只有在检测到违规和审核结束时才会触发回调。
RUNNING 输入参数
任务审核中,该状态会对单个图片或音频片段的检测结果进行回调。
FINISH 输入参数
任务审核结束,该状态下会返回前30个图片 / 音频违规片段结果,完整结果可通过 running 片段回调去拼接,或者调用查询任务详情接口来获取。
验签流程
为了验证接收到的回调数据是否来自合法的发送方并且在传输过程中未被篡改,用户在创建任务时可以选择向我们传递seed值,如果不需要验签则可以不用传递。在回调时,我们会加入签名参数以确保数据的安全性。我们将在返回的 HTTP 头部中添加
X-Signature
字段,其值将为 seed
+ body
的sha256编码和 Hex 字符串。{"TaskId": "task-video-X0zpcRUMzVidxj20","DataId":"test","Suggestion": "Block"}
则,审核完成后,我们会在调用 http://example.com 的时候,在 HTTP 头部 传入
X-Signature
的值为:74f0ae6d1f1e4eb1ffe4162da480a812f8a4dc19fe5a52bacbcd2c862d3edcfd
#!/usr/bin/env python3# -*- encoding: utf-8 -*-# 验签示例import hashlibseed = "dedb6dcc1cb7c63fde8fa5abfd57"body = '{"TaskId": "task-video-X0zpcRUMzVidxj20","DataId":"test","Suggestion": "Block"}'# 将 seed + body 转为 bytes 类型,并计算 SHA256 编码sha256 = hashlib.sha256((seed + body).encode('utf-8')).hexdigest()print(sha256)
回调 URL 续签
由于天御只具有临时访问用户 COS 桶的权限,临时权限最长只有 12 小时,因此,回调的 URL 有效期最长为 12 小时。建议在接收到回调时立即对 URL 进行签名更新和替换,以获取更长时效的链接。COS 目前支持的各语言 SDK 都提供了生成签名链接的功能,但获取长期时效链接需要使用 永久密钥。以下提供了 java、python、go 对应 SDK 调用的示例。
#!/usr/bin/env python3# -*- coding:utf-8 -*-from qcloud_cos import CosConfigfrom qcloud_cos import CosS3Client# 密钥secret_id = "" # 点击永久密钥获取secret_key = "" # 点击永久密钥获取region = "" # 默认 ap-guangzhoudef get_cos_presigned_url(url):"""获取......:param url::return: presigned_url"""# 获取客户端对象config = CosConfig(Region=region, SecretId=secret_id, SecretKey=secret_key)client = CosS3Client(config)# 根据key获取下载链接# 获取签名的cos桶bucket_name = "tianyu-live-video-content-moderation-xxxx"# print(bucket_name)# 获取签名的 COS 路径,请确保路径不被编码key = "segment-/audio/w-live_video-YrvumJyVBc3KM1DF/1656483481.mp3"# print(key)# 使用永久密钥时,expired_sec 签名过期时间可以随意设置expired_sec = 60 * 60 * 24 * 7download_response = client.get_presigned_download_url(Bucket=bucket_name, Key=key, Expired=expired_sec)return download_responseif __name__ == '__main__':# presigned_url既为获得新的签名的URLpresigned_url = get_cos_presigned_url("url")print(presigned_url)
import com.qcloud.cos.COSClient;import com.qcloud.cos.ClientConfig;import com.qcloud.cos.auth.BasicCOSCredentials;import com.qcloud.cos.auth.COSCredentials;import com.qcloud.cos.http.HttpMethodName;import com.qcloud.cos.http.HttpProtocol;import com.qcloud.cos.region.Region;import java.net.URL;import java.util.Date;import java.util.HashMap;import java.util.Map;public class TencentCosClient{public static void main(String [] args){// 调用 COS 接口之前必须保证本进程存在一个 COSClient 实例,如果没有则创建// 详细代码参见本页:简单操作 -> 创建 COSClientCOSClient cosClient = createCOSClient();// 获取签名的cos桶String bucketName = "tianyu-live-video-content-moderation-xxxx";// 获取签名的 COS 路径,请确保路径不被编码String key = "segment-/audio/w-live_video-YrvumJyVBc3KM1DF/1656483481.mp3";// 设置签名过期时间(可选), 若未进行设置则默认使用 ClientConfig 中的签名过期时间(1小时)// 这里设置签名在半个小时后过期Date expirationDate = new Date(System.currentTimeMillis() + 30 * 60 * 1000);// 填写本次请求的参数,需与实际请求相同,能够防止用户篡改此签名的 HTTP 请求的参数Map<String, String> params = new HashMap<String, String>();params.put("param1", "value1");// 填写本次请求的头部,需与实际请求相同,能够防止用户篡改此签名的 HTTP 请求的头部Map<String, String> headers = new HashMap<String, String>();headers.put("header1", "value1");// 请求的 HTTP 方法,上传请求用 PUT,下载请求用 GET,删除请求用 DELETEHttpMethodName method = HttpMethodName.GET;URL url = cosClient.generatePresignedUrl(bucketName, key, expirationDate, method, headers, params);System.out.println(url.toString());// 确认本进程不再使用 cosClient 实例之后,关闭之cosClient.shutdown();}// 创建 COSClient 实例,这个实例用来后续调用请求public static COSClient createCOSClient() {// 设置用户身份信息。// SECRETID 和 SECRETKEY 请登录访问管理控制台 https://console.cloud.tencent.com/cam/capi 进行查看和管理String secretId = "";String secretKey = "";COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);// ClientConfig 中包含了后续请求 COS 的客户端设置:ClientConfig clientConfig = new ClientConfig();// 设置 bucket 的地域// COS_REGION 请参照 https://cloud.tencent.com/document/product/436/6224clientConfig.setRegion(new Region("ap-guangzhou"));// 设置请求协议, http 或者 https// 5.6.53 及更低的版本,建议设置使用 https 协议// 5.6.54 及更高版本,默认使用了 httpsclientConfig.setHttpProtocol(HttpProtocol.https);// 以下的设置,是可选的:// 设置 socket 读取超时,默认 30sclientConfig.setSocketTimeout(30*1000);// 设置建立连接超时,默认 30sclientConfig.setConnectionTimeout(30*1000);// 如果需要的话,设置 http 代理,ip 以及 portclientConfig.setHttpProxyIp("httpProxyIp");clientConfig.setHttpProxyPort(80);// 生成 cos 客户端。return new COSClient(cred, clientConfig);}}
package mainimport ("context""fmt""net/http""net/url""time""github.com/tencentyun/cos-go-sdk-v5")// 通过tag的方式,用户可以将请求参数或者请求头部放进签名中。type URLToken struct {SessionToken string `url:"x-cos-security-token,omitempty" header:"-"`}func GetCosUrl() {// 替换成您的密钥tak := ""tsk := ""tmpURL := "https://tianyu-live-video-content-moderation-xxxx/segment-/audio/w-live_video-YrvumJyVBc3KM1DF/1656483481.mp3?X-Amz-Algorithmxxxx"parse, err := url.Parse(tmpURL)if err != nil {fmt.Println(err)return}// 获取签名的cos桶 tianyu-live-video-content-moderation-xxxxbucket_name := parse.Scheme + "://" + parse.Host// 获取签名的 COS 路径,请确保路径不被编码 /segment-/audio/w-live_video-YrvumJyVBc3KM1DF/1656483481.mp3key := parse.Pathu, _ := url.Parse(bucket_name)b := &cos.BaseURL{BucketURL: u}c := cos.NewClient(b, &http.Client{})name := keyctx := context.Background()// 通过 tag 设置 x-cos-security-token// Get presignedpresignedURL, err := c.Object.GetPresignedURL(ctx, http.MethodGet, name, tak, tsk, 100*time.Hour, nil)if err != nil {fmt.Printf("Error: %v\\n", err)return}// Get object by presinged urlresp, err := http.Get(presignedURL.String())if err != nil {fmt.Printf("Error: %v\\n", err)}defer resp.Body.Close()fmt.Println(presignedURL.String())fmt.Printf("resp:%v\\n", resp)}
回调异常 FAQ
未收到回调结果一般是什么原因?
1. 任务还在 Pending 中,可以查询任务详情接口看 Status 状态。任务审核逻辑是新增任务优先审核,旧任务往后排队。
2. 当配置为最终结果回调时,只有在审核任务 Finish 结束后才会触发回调。您可以通过详情接口查看 Status 任务状态,也可以通过控制台修改回调逻辑,以支持片段和最终结果的回调。
3. 用户侧未按照正确的响应码响应200,导致审核回调失败,可以使用类似 Postman 或 curl 等工具发送请求,并查看响应代码是否为200。
4. 回调等待响应头时超时,回调超时时间超过5s会报错"context deadline exceeded (Client.Timeout exceeded while awaiting headers)" ,该情况需要提供taskid 给到产品侧确认。
检查回调服务是否正常运行,并确保其能够及时响应请求。
检查网络连接是否稳定,并尝试使用更快的网络连接。
如果请求被防火墙或其他网络安全设备拦截,可以尝试修改其配置,以允许请求通过。
建议客户侧可以加上调试和监控功能系统。可以使用日志记录来记录请求和响应数据,以便进行故障排除。您还可以使用监控工具来监视您的回调服务的性能和可用性。
回调 URL 为什么无法打开?
如果用户的接收程序对回调的内容进行了编码,会导致链接无法正确打开。