php文件本身不是视频,不能“变成”mp4;所谓“php转mp4后损坏”,本质是服务器将本该直传的mp4二进制数据交由php解析器执行,导致头部被插入php输出、bom、错误提示或空白字符,使mp4关键box偏移或覆盖而损坏。

PHP 本身不处理视频解码或修复,所谓“PHP 转 MP4 时视频损坏”,95% 是输出污染、moov 位置错误、或 FFmpeg 命令执行失败导致的逻辑损坏——不是文件真丢了像素,而是播放器找不到关键元数据。修复必须从字节流和命令执行两个层面下手。
readfile() 输出 MP4 时文件头被污染怎么办
常见错误现象:下载的 MP4 文件用文本编辑器打开,开头能看到 <?php 、Warning:、空白行,或根本搜不到 ftyp 字符串;用 file -i broken.mp4 返回 text/plain。
- 检查 PHP 文件本身:确保
.php文件无 BOM、无空行(尤其文件末尾换行后不能有多余空格) - 禁用所有可能的前置输出:关闭
output_buffering、不用echo/print、不在readfile()前调用任何函数 - 强制清空缓冲并设置严格头:
ob_end_clean(); header('Content-Type: video/mp4'); header('Content-Length: '.filesize($path)); readfile($path); exit; - 若仍失败,说明 Web 服务器把
.php后缀误交给了 PHP 解析器——临时改 Nginx 配置:location ~ \.php$ { try_files $uri =404; },让其当静态文件直传
FFmpeg 生成的 MP4 无法拖拽/黑屏/花屏
本质是 moov atom(视频索引)没放在文件开头,或编码参数不兼容 Web 播放。浏览器加载时只能顺序读,一拖进度条就卡死或重播。
- 必须加
-movflags +faststart:它会把 moov 移到文件头部,命令示例:ffmpeg -i input.mp4 -c copy -movflags +faststart output.mp4 - 避免用
-c copy直接封装非标准源:某些 AVI 或 MOV 源含私有编码,copy 会导致播放器解码失败;应显式转码:-c:v libx264 -c:a aac -pix_fmt yuv420p - 检查生成结果是否真有效:
ffprobe -v quiet -show_entries format=duration,input_filename output.mp4,若报错或 duration 为空,说明 FFmpeg 实际没成功写完 - PHP 中别只看
$return_code === 0:有些 FFmpeg 错误(如磁盘满、权限不足)仍返回 0,必须同时检查$output数组里有没有error或failed关键字
PHP 调用 exec() 执行 FFmpeg 失败的典型原因
不是命令写错了,而是环境权限和参数安全机制在拦截。PHP 8+ 对 shell 注入更敏感,老写法大概率失效。
- 绝对路径必须:用
which ffmpeg查真实路径,硬编码进 PHP,别依赖$PATH;例如:/usr/local/bin/ffmpeg而非ffmpeg - 用户权限要对:Web 进程用户(如
www-data)需有读输入文件、写输出目录、执行ffmpeg的权限;ls -l $(which ffmpeg)和ls -ld /output/dir必须显示可执行/可写 - 参数必须转义:所有路径变量必须套
escapeshellarg(),哪怕路径看起来“很干净”;用户上传的文件名含空格、括号、单引号时,不转义直接崩溃 - 别用
shell_exec()拼字符串:改用proc_open()+ 数组传参,能更好捕获 stderr 并控制超时;PHP 8.5.5 对exec()的返回类型检查更严,模糊写法易报TypeError
前端保存 PHP 输出的 MP4 流时出错
浏览器拿到的是 HTTP 响应体,但没正确识别为二进制 blob——结果保存下来是乱码或 0KB,不是真视频。
- 后端必须设
Content-Disposition: attachment; filename="xxx.mp4",否则 Chrome 可能按 HTML 渲染 - 前端用
fetch()获取响应时,必须调用response.blob(),再用URL.createObjectURL()创建链接;直接response.text()会破坏二进制 - 大文件慎用一次性
blob():内存压力大,可用ReadableStream分块读取 +WritableStream写入文件系统(仅现代浏览器支持) - 若 PHP 是通过
fpassthru()流式输出,务必确认没启用zlib.output_compression,否则 gzip 压缩会彻底破坏 MP4 二进制结构
真正难的不是写对一行 exec(),而是搞清每一层(Web 服务器、PHP 进程、FFmpeg、浏览器)分别在哪个环节悄悄改了字节——一个空格、一个未关闭的 buffer、一个没校验的返回码,都足以让 MP4 在播放器里变成黑屏或报错。动手前先用 file 和 hexdump -C 看两眼原始字节,比盲目重试高效得多。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











