应使用fileinfo扩展读取文件头识别真实类型,调用ffprobe解析元数据分类,并通过可配置规则数组匹配duration、tags.artist等字段实现业务驱动的自动归类。

PHP上传后怎么识别音频文件类型
光靠文件扩展名不可靠,$_FILES['audio']['type'] 也常被浏览器伪造。真正稳妥的方式是读取文件头(magic bytes)或调用系统命令分析。推荐用 fileinfo 扩展——它基于 libmagic,能准确识别 MP3、FLAC、WAV、OGG 等真实格式。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 确认已启用:
extension=fileinfo在php.ini中开启,运行php -m | grep fileinfo验证 - 上传成功后立即检测:
$finfo = finfo_open(FILEINFO_MIME_TYPE); $mimeType = finfo_file($finfo, $_FILES['audio']['tmp_name']); finfo_close($finfo);
- 注意:
$mimeType返回类似audio/mpeg或audio/flac,不是audio/mp3—— 后者根本不是标准 MIME 类型 - 别跳过验证:即使前端限制了
accept="audio/*",攻击者仍可改 POST 请求绕过
如何用PHP提取音频元数据做分类依据
仅靠 MIME 类型不够区分“播客”“音乐”“会议录音”。需要读取 ID3(MP3)、Vorbis Comments(OGG)、FLAC metadata 等嵌入信息。PHP 原生不支持深度解析,得靠外部工具或轻量库。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 优先用
ffprobe(FFmpeg 工具集):它输出结构化 JSON,字段清晰,兼容性远超 PHP 扩展$cmd = 'ffprobe -v quiet -print_format json -show_format -show_streams "' . escapeshellarg($_FILES['audio']['tmp_name']) . '"'; $result = json_decode(shell_exec($cmd), true);
- 关键分类字段:
$result['format']['tags']['title']、$result['format']['tags']['artist']、$result['streams'][0]['codec_type'] === 'audio'、$result['format']['duration'](秒数,可判是否为长语音) - 避免用
getID3库:虽纯 PHP,但对新版 ID3v2.4 和 Opus 支持不稳定,且解析耗时高,大文件易超时 - 务必加超时和错误处理:
shell_exec()要配合set_time_limit(30),并检查$result是否为空或含error键
按业务规则自动分到不同目录的逻辑怎么写
分类不是技术问题,是业务映射问题。PHP 只负责执行判断,规则必须可配置、易调整,不能硬编码在 if-else 里。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 定义规则数组,按优先级顺序匹配:
$rules = [ ['field' => 'duration', 'op' => '>', 'value' => 1800, 'target' => 'podcast'], ['field' => 'tags.artist', 'op' => '==', 'value' => 'Internal Team', 'target' => 'meeting'], ['field' => 'format.codec_name', 'op' => 'in', 'value' => ['opus', 'aac'], 'target' => 'voicemail'], ]; - 用递归数组访问函数提取嵌套值(如
tags.artist),别用多层isset($a['b']['c']['d'])判空 - 目标目录要白名单校验:
in_array($targetDir, ['podcast', 'music', 'meeting'], true),防止路径遍历 - 移动前先
mkdir("uploads/{$targetDir}", 0755, true),确保父目录存在;用move_uploaded_file()而非copy(),更安全
为什么上传完立刻转码再分类反而更麻烦
有些方案主张“统一转成 MP3 再分析”,这会引入新问题:转码失败率高、耗 CPU、丢失原始元数据、延长响应时间。除非业务强依赖统一格式(如只支持 MP3 播放器),否则不建议。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 分类和转码应解耦:先按原始格式分类,再由队列异步转码(如用
supervisor + beanstalkd) - 如果必须实时转码,用
-vn -acodec libmp3lame -ar 44100 -ab 128k这类保守参数,避免-q:a 0这种高压缩导致 FFmpeg 崩溃 - 注意:转码后的文件
ffprobe输出中format.tags通常为空——原始 ID3 不会自动复制过去,需额外用ffmpeg -i in.mp3 -i in.mp3 -c copy -map 0 -map_metadata 1 out.mp3 - 别在 Web 请求周期内做转码:超时风险极高,Nginx/Apache 默认 60 秒超时,大文件必失败
最易被忽略的一点:音频文件可能无任何元数据(比如手机直录的 WAV),此时分类只能依赖 MIME 类型 + 文件大小 + 采样率等硬指标,规则表必须有兜底分支(如 default => 'other')。



















