PHP 怎么识别上传文件的 MIME 类型
PHP 识别上传文件的 MIME 类型,应该读取服务器保存的临时文件,而不是直接相信浏览器传来的 $_FILES['upload']['type'] 或原始扩展名。最小写法是 new finfo(FILEINFO_MIME_TYPE),再调用 file($_FILES['upload']['tmp_name'])。得到 image/jpeg、image/png 这类纯 MIME 值后,与服务端允许列表比较,最后才决定是否保存。
- 先检查
$_FILES['upload']['error'],成功后再读取tmp_name。 - 用
finfo(FILEINFO_MIME_TYPE)判断文件内容,不把客户端的type当成安全结论。 - MIME 允许列表、大小限制、随机文件名和
move_uploaded_file()要组成一条完整链路。
先读上传临时文件,不要相信客户端 MIME
上传表单提交后,PHP 会把文件放到服务器临时目录,并在 $_FILES 中给出 name、type、size、tmp_name 和 error。其中 type 是浏览器提供的 MIME 提示,PHP 手册明确提醒它没有在服务端校验;真正用于判断的输入应是服务器临时文件。

因此,顺序不要写成“看扩展名就保存”。先看错误码,再确认临时文件确实由 PHP 上传产生,最后交给 Fileinfo。这样即使用户把一个脚本改名成 photo.jpg,文件名也不会直接改变判断依据。
用 finfo 获取纯 MIME,并和允许列表比较
FILEINFO_MIME_TYPE 返回不带字符集的 MIME 类型,适合做白名单比较;FILEINFO_MIME 可能附带字符集,例如 text/plain; charset=us-ascii,更适合展示或记录。上传接口通常需要前者。
| 字段或函数 | 用途 | 安全判断 |
|---|---|---|
$_FILES['upload']['name'] | 客户端原始文件名 | 不直接拼接到保存路径 |
$_FILES['upload']['type'] | 浏览器上报的 MIME 提示 | 不能单独作为校验结果 |
tmp_name | 服务器临时文件路径 | 交给 finfo 读取内容 |
finfo(FILEINFO_MIME_TYPE) | 识别文件内容类型 | 和服务端允许列表严格比较 |

示例中,$allowed 是接口自己的业务边界,而不是“所有被 finfo 识别出来的类型都允许”。例如头像接口只接受三种图片 MIME;PDF 上传接口应维护另一份列表,不能把同一套规则到处复用。
把类型判断放进完整上传链路
下面的例子把文件上传到当前目录的 uploads 子目录。它先处理 PHP 上传错误,再检查大小、来源、MIME 和保存结果。生产环境还应把上传目录放到不能直接执行脚本的位置,并把拒绝原因写入应用日志。
$maxBytes || !is_uploaded_file($file['tmp_name'])) {
throw new RuntimeException('文件大小或上传来源不符合要求');
}
$finfo = new finfo(FILEINFO_MIME_TYPE); // 从服务器临时文件读取内容类型。
$mime = $finfo->file($file['tmp_name']);
$extensions = [
'image/jpeg' => 'jpg',
'image/png' => 'png',
'image/gif' => 'gif',
];
if ($mime === false || !isset($extensions[$mime])) {
throw new RuntimeException('不允许的文件类型');
}
$uploadDir = __DIR__ . '/uploads';
if (!is_dir($uploadDir) && !mkdir($uploadDir, 0750, true)) {
throw new RuntimeException('上传目录创建失败');
}
$target = $uploadDir . '/' . bin2hex(random_bytes(16)) . '.' . $extensions[$mime];
if (!move_uploaded_file($file['tmp_name'], $target)) {
throw new RuntimeException('文件保存失败');
}
echo '上传成功';
?>
这里的扩展名来自 MIME 白名单映射,不来自用户输入。随机文件名避免了原始名称中的路径片段和特殊字符;move_uploaded_file() 只负责把已经通过判断的临时文件移动到目标位置,不能替代前面的类型和大小检查。
处理 finfo 误判、空文件和扩展名陷阱
MIME 识别不是“图片安全检测”的同义词。损坏文件、极短文件或格式内部带有异常内容时,识别结果可能是 false、通用二进制类型,或者与业务预期不一致。对图片服务,必要时还要在解码阶段确认尺寸、重新编码,并限制资源消耗。
另外,UPLOAD_ERR_OK 只说明 PHP 这次上传流程成功,不代表文件类型合规;finfo 返回图片 MIME 也不意味着可以信任原始文件名。日志中建议记录请求标识、识别出的 MIME、文件大小和拒绝原因,避免记录完整临时路径或用户提交的敏感名称。
常见问题
为什么不直接用 $_FILES['upload']['type']?
它由客户端提供,只能作为展示提示。服务端应读取 tmp_name,使用 finfo 自己判断。
FILEINFO_MIME 和 FILEINFO_MIME_TYPE 有什么区别?
前者可能返回 MIME 加字符集,后者只返回 MIME 类型。做精确白名单比较时通常选择 FILEINFO_MIME_TYPE。
finfo 判断通过后还要检查扩展名吗?
可以检查,但扩展名应由服务端根据允许的 MIME 映射生成,而不是拿用户提交的扩展名决定是否放行。
Go 泛型约束里的 int 和波浪号 int 有什么区别
- 上一篇
- Go 泛型约束里的 int 和波浪号 int 有什么区别
- 下一篇
- Go 项目怎么打包依赖以支持离线构建
-
- 文章 · php教程 | 2小时前 |
- PHP 打包 ZIP 怎么去掉服务器目录前缀
- 168浏览 收藏
-
- 文章 · php教程 | 3小时前 |
- PHP cURL 连接超时和请求总超时怎么分别设置
- 498浏览 收藏
-
- 文章 · php教程 | 4小时前 | JSON · php教程 · 接口开发 · 数组处理 · php JSON array_values array_filter json_encode
- PHP array_filter 后 JSON 为什么变成对象
- 132浏览 收藏
-
- 文章 · php教程 | 6小时前 |
- PHP yield 读取大量数据为什么能节省内存
- 495浏览 收藏
-
- 文章 · php教程 | 7小时前 | 时区 · 后端开发 · php教程 · 日期时间 · 不可变对象 · php 时区转换 DateTimeZone DateTimeImmutable setTimezone
- PHP 怎么在不修改原对象的情况下转换时区
- 184浏览 收藏
-
- 文章 · php教程 | 8小时前 |
- PHP 登录时怎么升级旧密码哈希
- 125浏览 收藏
-
- 文章 · php教程 | 9小时前 |
- PHP 同一用户的多个请求为什么排队执行
- 373浏览 收藏
-
- 文章 · php教程 | 10小时前 | JSON · PHP · json_decode · JSON_BIGINT_AS_STRING ·
- PHP 解析 JSON 长整数怎么保留完整数字
- 220浏览 收藏
-
- 文章 · php教程 | 22小时前 |
- PHP 大 CSV 文件怎么逐行导入并跳过空行
- 306浏览 收藏
-
- 文章 · php教程 | 1天前 | 性能优化 · PHP · 图片处理 · php 缩略图 memory_limit GD
- PHP 缩略图一到大尺寸就内存溢出:按像素占用和处理阶段拆分 GD 优化
- 332浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- SuperCLUE
- SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
- 162次使用
-
- C-Eval
- 深入了解C-Eval中文评估套件,涵盖52个学科与4级难度。本文详解其功能特点、Zero-shot/Few-shot使用方法及代码示例,助您全面评测LLM中文理解与泛化能力。
- 88次使用
-
- LangGPT
- LangGPT是一种受编程语言启发的结构化提示词设计工具,提供双层框架、模块化模板及变量功能,帮助用户高效编写高质量Prompt。该项目已在GitHub免费开源,适用于内容创作、编程辅助等多场景。
- 12次使用
-
- ClickPrompt
- ClickPrompt是一款专为AI提示词编写者设计的开源在线工具,支持Stable Diffusion绘图、ChatGPT对话及GitHub Copilot代码辅助。提供Prompt自动生成、一键运行、社区分享及可视化优化功能,帮助用户高效获取精准AI输出。
- 49次使用
-
- PromptHero
- PromptHero是专业的AI提示词搜索引擎与优化平台,支持Stable Diffusion、Midjourney等主流模型。提供海量提示词库、分类搜索、在线课程及社区互动,助力用户高效生成高质量AI图像与文本。
- 32次使用
-
- Go语言实现文件上传
- 2023-01-23 255浏览
-
- golang gin框架实现大文件的流式上传功能
- 2022-12-25 459浏览
-
- Go语言线程安全之互斥锁与读写锁
- 2022-12-27 346浏览
-
- golang实现PHP数组特性的方法
- 2023-02-16 371浏览
-
- Golang实现http文件上传小功能的案例
- 2023-01-07 282浏览

