本文介绍一种可靠方法,结合文件扩展名与 MIME 类型检测,精准区分上传文件是图像、视频还是文档类型,避免仅依赖后缀或 getimagesize() 导致的误判。
本文介绍一种可靠方法,结合文件扩展名与 mime 类型检测,精准区分上传文件是图像、视频还是文档类型,避免仅依赖后缀或 `getimagesize()` 导致的误判。
在 PHP 文件上传处理中,仅通过文件扩展名(如 .mp4、.jpg)或 getimagesize() 判断类型存在明显缺陷:前者易被伪造,后者对非图像文件会直接返回 false 或警告,且完全无法识别视频——因为 getimagesize() 仅支持图像格式(GIF/JPEG/PNG 等),对 MP4、MOV、AVI 等视频文件无响应。
更健壮的方案是
双重校验
:先提取扩展名作初步筛选,再调用 mime_content_type()(或更推荐的 finfo_open())读取文件真实 MIME 类型,实现内容级识别。
以下是一个生产就绪的分类函数示例:
✅
关键优势说明
:
PHP 8.5.5
PHP 8.5.5 是 PHP 8.5 分支的维护更新版本。该版本延续了“小步快跑”的迭代逻辑,通过深度错误修复、底层性能微调以及安全加固,旨在为开发者提供一个更健壮、更高效的运行环境。该版本严格遵守语义化版本规范,不包含破坏性变更。
下载
立即学习
“
PHP免费学习笔记(深入)
”;
使用 finfo_open(FILEINFO_MIME_TYPE) 替代已废弃的 mime_content_type(),更稳定、更准确,且支持自定义魔数规则;
mime_patterns 支持字符串前缀(如 'video/')而非硬编码完整 MIME,兼容不同编码/封装格式(如 video/quicktime 对应 .mov);
扩展名预筛减少 finfo 调用频次,兼顾性能与安全性;
显式校验 is_uploaded_file() 防止路径遍历或临时文件伪造攻击。
⚠️
注意事项
:
确保服务器启用 fileinfo 扩展(PHP 默认开启,可通过 extension=fileinfo 确认);
不要仅依赖 $_FILES['file']['type'] —— 该字段由客户端浏览器提供,极易被篡改;
对于高安全要求场景(如用户头像、富媒体上传),建议额外结合 getimagesize()(图像)、ffprobe(视频)或 pdftotext(PDF)做二次验证;
始终对上传目录设置严格权限(如 chmod 644 临时文件),并重命名文件以规避执行风险。
通过此方法,你将获得一个鲁棒、可维护、符合现代 PHP 最佳实践的文件类型识别模块,为后续业务逻辑(如转码、OCR、存储策略)奠定坚实基础。
function classifyUploadedFile($tmpPath): string
{
if (!is_uploaded_file($tmpPath) || !file_exists($tmpPath)) {
throw new InvalidArgumentException('Invalid uploaded file path.');
}
$ext = strtolower(pathinfo($tmpPath, PATHINFO_EXTENSION));
// 预定义类型映射(可按需扩展)
$mappings = [
'image' => [
'extensions' => ['gif', 'jpg', 'jpeg', 'png', 'webp', 'bmp'],
'mime_patterns' => ['image/']
],
'video' => [
'extensions' => ['mp4', 'mov', 'avi', 'mkv', 'webm', 'flv'],
'mime_patterns' => ['video/']
],
'document' => [
'extensions' => ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'csv'],
'mime_patterns' => ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument', 'text/plain', 'text/csv']
]
];
// 第一步:扩展名快速过滤(提升性能)
foreach ($mappings as $type => $config) {
if (in_array($ext, $config['extensions'])) {
// 第二步:MIME 内容检测(关键!)
$finfo = finfo_open(FILEINFO_MIME_TYPE);
if ($finfo === false) {
throw new RuntimeException('Failed to initialize fileinfo.');
}
$mime = finfo_file($finfo, $tmpPath);
finfo_close($finfo);
if ($mime === false) {
return 'unknown';
}
// 支持前缀匹配(如 'video/' 匹配 'video/mp4'、'video/quicktime')
foreach ($config['mime_patterns'] as $pattern) {
if (strpos($mime, $pattern) === 0) {
return $type;
}
}
}
}
return 'unknown';
}
// 使用示例
if ($_FILES['file']['error'] === UPLOAD_ERR_OK) {
$tmpFile = $_FILES['file']['tmp_name'];
$category = classifyUploadedFile($tmpFile);
switch ($category) {
case 'image':
echo "✅ 处理图像文件:支持缩略图生成、EXIF 提取等";
break;
case 'video':
echo "✅ 处理视频文件:可触发 FFmpeg 转码或元信息解析";
break;
case 'document':
echo "✅ 处理文档文件:适配 PDF 文本提取或 Office 解析";
break;
default:
echo "❌ 不支持的文件类型,请上传 JPG/PNG/MP4/PDF 等标准格式";
}
}