PHP处理音频上传回调需先校验签名或Token防伪造,再根据Content-Type用file_get_contents('php://input')读原始数据并解析JSON或表单,最后异步转码、校验OSS callbackVar字段并配置服务器超时。

PHP接收音频上传后的回调请求,关键在验证来源和解析数据
不是所有“回调”都可信,音频服务(比如腾讯云、七牛、阿里云OSS)发来的回调请求必须校验签名或Token,否则直接写入数据库或触发转码可能被伪造请求打穿业务逻辑。
典型错误现象:file_get_contents('php://input') 读不到数据、$_POST 为空、回调返回400但服务端日志没报错——大概率是对方用application/json发的POST,而你只处理了multipart/form-data表单。
- 先检查
$_SERVER['CONTENT_TYPE'],常见值有application/json、application/x-www-form-urlencoded、multipart/form-data - 用
file_get_contents('php://input')读原始体(注意:不能和$_POST混用,PHP会清空一次) - 若为JSON,用
json_decode($raw, true)解析;若为表单编码,用parse_str($raw, $data) - 务必校验
X-Signature、Authorization或查询参数里的sign/token,算法参考对应平台文档(如HMAC-SHA256 + 密钥)
七牛云音频上传回调的PHP校验示例
七牛要求回调URL带upload_token签名,且请求头含X-Qiniu-Zone,但真正防刷的是X-Qiniu-Signature和X-Qiniu-Date组合校验。
实操要点:
立即学习“PHP免费学习笔记(深入)”;
- 从
$_SERVER['HTTP_X_QINIU_SIGNATURE']取签名值 - 拼接
$_SERVER['HTTP_X_QINIU_DATE'] . "\n" . file_get_contents('php://input')作为待签原文 - 用你配置的
access_key和secret_key生成HMAC-SHA1签名,再Base64编码,与头中签名比对 - 七牛回调体是JSON格式,字段如
key(文件名)、hash(ETag)、fsize(字节大小),可直接存库或丢进队列
$raw = file_get_contents('php://input');
$sign = $_SERVER['HTTP_X_QINIU_SIGNATURE'] ?? '';
$date = $_SERVER['HTTP_X_QINIU_DATE'] ?? '';
$expected = base64_encode(hash_hmac('sha1', $date . "\n" . $raw, $secret_key, true));
if (!hash_equals($expected, $sign)) {
http_response_code(401);
exit('Invalid signature');
}
$data = json_decode($raw, true);
// $data['key'] 就是音频文件在七牛的路径
回调里触发FFmpeg转码要注意进程和超时
别在回调HTTP响应前直接跑exec('ffmpeg -i ...'),PHP默认执行超时是30秒,音频文件大时必然超时,而且阻塞Web进程影响并发。
- 用
shell_exec("nohup ffmpeg ... > /dev/null 2>&1 & echo $!")后台运行,但需确保PHP用户有权限执行ffmpeg - 更稳妥做法:把转码任务写入Redis队列或MySQL任务表,由独立Worker进程消费(例如用
supervisord管理的PHP CLI脚本) - 记录
task_id和原始key,后续用Webhook或轮询查状态,避免回调接口卡死 - FFmpeg命令加
-v quiet -stats减少输出干扰,加-y跳过确认
阿里云OSS回调的callback-var怎么在PHP里取到
OSS允许在上传策略中定义callback-var,比如{'x:uid':'123','x:scene':'voice'},这些变量会以JSON形式出现在回调Body的body字段里,不是顶层键。
典型结构:
{
"bucket": "my-audio-bucket",
"object": "2024/05/voice_abc.mp3",
"size": 2048000,
"mimeType": "audio/mpeg",
"imageInfo": null,
"callbackVar": {"x:uid":"123","x:scene":"voice"}
}
所以必须先解一层callbackVar:
- 用
json_decode($raw, true)得到数组后,取$data['callbackVar']['x:uid'] - OSS回调默认不带签名,靠
callbackUrl的Token参数防刷,建议在URL里加?token=sha256($bucket.$secret)并在PHP里校验 - 注意
callbackVar里的键名带冒号,PHP数组访问必须用引号包裹字符串键
fastcgi_read_timeout、Apache的Timeout),导致明明代码跑完了,客户端却收到504。



















