编写PHP代码的资深开发者都很清楚,$_FILES['video']['error'] 是捕获上传失败的首要判断依据。然而,它仅返回一个整数错误码,例如 UPLOAD_ERR_INI_SIZE(对应值1)。究竟是磁盘空间不足、扩展名被拒绝,还是 upload_max_filesize 限制超限,单凭这个数字很难直接定位问题。依赖猜测进行调试效率极低,必须建立一套系统化的排查方案。
即便上传成功,也不能放松警惕。必须使用 ffprobe 工具验证视频文件是否真实有效,否则仅靠检查 MIME 类型很容易被伪造的 .mp4 文件绕过安全检测。

视频上传失败时如何捕获具体错误信息
处理视频文件上传失败时,$_FILES['video']['error'] 是首要判断依据,但它仅返回整数错误码,缺少上下文信息。直接执行 var_dump($_FILES) 往往难以定位问题根源——例如磁盘已满、扩展名被拒绝、upload_max_filesize 限制超限等情况,都会映射到同一个 UPLOAD_ERR_INI_SIZE(值为1)。
实操建议:
- 首先检查
$_FILES['video']['error']的返回值,并对照 PHP 官方预定义常量:包括UPLOAD_ERR_OK、UPLOAD_ERR_INI_SIZE、UPLOAD_ERR_FORM_SIZE、UPLOAD_ERR_PARTIAL、UPLOAD_ERR_NO_FILE、UPLOAD_ERR_NO_TMP_DIR、UPLOAD_ERR_CANT_WRITE、UPLOAD_ERR_EXTENSION等。 - 如果错误码是
UPLOAD_ERR_INI_SIZE或UPLOAD_ERR_FORM_SIZE,必须同步核查ini_get('upload_max_filesize')配置值与表单隐藏域MAX_FILE_SIZE是否一致,并且确保其数值足够容纳上传文件。 - 若错误码为
UPLOAD_ERR_CANT_WRITE,很可能是临时目录(可通过sys_get_temp_dir()获取)缺少写入权限或磁盘空间不足,与视频文件本身无关。 - 上传成功后,应立即使用
getimagesize($tmp_path)或借助系统命令ffprobe验证文件是否为真实的有效视频,以防恶意伪造的 .mp4 文件绕过基础的 MIME 类型检查。
ffmpeg执行失败时如何获取详细错误信息
使用 exec() 或 shell_exec() 调用 ffmpeg 处理视频时,错误信息往往会被静默忽略。默认情况下,标准错误输出(stderr)不会被捕获,$output 仅包含标准输出(stdout),而 ffmpeg 的关键错误(如编解码器不支持、文件损坏)全都输出到 stderr 上。
实操建议:
- 务必通过重定向将 stderr 合并到 stdout:
exec("ffmpeg -i '$input' -c:v libx264 '$output' 2>&1", $output, $return_code),否则$output将为空数组。 $return_code非零才表示执行失败,但仅凭返回值无法定位具体原因;必须仔细查看$output中的内容,它包含了 ffmpeg 输出的原始错误信息,例如"Invalid data found when processing input"或"Unknown encoder 'libx264'"等提示。- 在生产环境中如果禁止使用
exec(),则应改用proc_open()来精确控制 stdin/stdout/stderr 流,并设置超时时间(视频转码过程可能出现卡死),以防止产生僵尸进程。 - 不要仅依赖
mime_content_type()来判断视频格式——该函数只检查文件头,对于经过剪辑的 MP4 或 WebM 文件容易产生误判;真正的格式校验应当调用ffprobe -v quiet -show_entries format=duration -of default "$file"并解析其输出结果。
PHP中如何记录视频处理全流程日志
仅靠单一的 error_log() 调用无法有效支撑视频任务的排错需求:你需要明确“是哪个用户、上传了哪个文件、在哪个环节(上传/校验/转码/截图)出现异常、耗时多少、返回了什么错误”。采用硬编码拼接字符串的方式容易遗漏字段,且难以解析。
实操建议:
- 采用结构化日志格式:至少应包含
timestamp(时间戳)、user_id(用户标识)、file_name(文件名)、stage(处理阶段,如 'upload'/'validate'/'transcode')、duration_ms(耗时毫秒数)、error(如有错误信息)、extra(附加信息,如 ffmpeg 返回码、临时文件路径)等字段。 - 避免直接写入文件,建议优先使用
MonologLogger配合StreamHandler,并将日志格式设置为JsonFormatter,这样便于后续通过 ELK 或 grep 工具进行解析检索。 - 在关键节点手动添加日志记录:上传完成后记录一条,
ffprobe校验后记录一条,ffmpeg处理开始前记录一条,处理结束后再记录一条——不要等到最后统一记录,否则中间环节一旦失败就会丢失日志。 - 日志文件路径需要进行隔离管理:按日期分目录存储,例如
/var/log/video/2026-06-18.log,避免单个日志文件过大;同时要确保www-data用户对该路径拥有写入权限(常见陷阱:日志目录属主被设置为 root)。
set_error_handler为何对视频操作无效?原因解析
set_error_handler() 无法捕获视频处理中的大多数问题,因为其作用域仅局限在 PHP 运行时警告(E_WARNING)、通知(E_NOTICE)等层面,而视频相关的错误基本不属于这个范畴:
实操说明:
$_FILES的错误码由 CGI 协议层返回的整数,并非 PHP 错误类型,因此set_error_handler完全无法捕获。- ffmpeg 子进程发生崩溃、超时或被信号终止(如 SIGKILL)时,PHP 层仅能获取到一个非零的
$return_code,并不会触发任何 PHP 错误。 - 使用 GD 或 Imagick 库处理视频帧截图时抛出的
Exception,属于异常机制而非错误,必须通过try-catch结构捕获,这不在set_error_handler的管辖范围之内。 - 实际上,真正应该使用
set_error_handler的场景,是在自定义函数中主动调用trigger_error()——例如你封装的validateVideoDuration()函数检测到视频时长超出限制时,可以执行trigger_error("视频时长超出30分钟限制", E_USER_WARNING),这时才能被自定义错误处理器捕获。
视频处理流程链条较长、涉及跨进程调用、且高度依赖外部工具,错误分布广泛,涵盖协议层、系统层、子进程层以及应用逻辑层——没有一劳永逸的解决方案,必须逐层加强防护与日志记录,任何一环的遗漏都会导致排查时只能依赖猜测。
