PHP文件上传必须检查$_FILES数组结构,其键名需与HTML表单name属性一致,仅$_FILES'field'可用于move_uploaded_file(),且须校验error、size、真实MIME、扩展名白名单,并配置好目录权限与Web服务器解析策略。

PHP 文件上传必须检查 $_FILES 数组结构
PHP 上传文件后,所有元信息都存在 $_FILES 中,不是 $_POST 或 $_GET。初学者常直接读 $_POST['file'],结果永远为空。
一个标准的 提交后,$_FILES 会是这样的结构:
Array
(
[avatar] => Array
(
[name] => photo.jpg
[type] => image/jpeg
[tmp_name] => /tmp/phpABC123
[error] => 0
[size] => 204800
)
)
注意:键名 avatar 必须和 HTML 表单中 name 属性完全一致;[tmp_name] 是唯一能用于移动文件的临时路径;[error] 为 0 才表示上传成功。
-
[error]值非 0 时(如 1 表示超upload_max_filesize),不能调用move_uploaded_file() -
[tmp_name]只在当前请求生命周期有效,下一次请求就失效 - 多文件上传时(
name="files[]"),$_FILES['files']是按字段维度组织的二维数组,不是按文件顺序
用 move_uploaded_file() 而不是 copy() 或 rename()
move_uploaded_file() 是 PHP 唯一安全处理上传文件的函数。它内部做了校验:只允许从 $_FILES[...]['tmp_name'] 移动,拒绝任何伪造路径或本地文件。
立即学习“PHP免费学习笔记(深入)”;
错误写法示例(危险):
$source = $_POST['fake_path']; // 攻击者可控制 rename($source, '/var/www/uploads/hacked.php');
正确做法必须基于 $_FILES 的 tmp_name:
$uploadDir = '/var/www/uploads/';
$uploadFile = $uploadDir . basename($_FILES['avatar']['name']);
if ($_FILES['avatar']['error'] === 0) {
if (move_uploaded_file($_FILES['avatar']['tmp_name'], $uploadFile)) {
echo "上传成功";
}
}
- 目标路径必须提前创建好目录并确保 Web 进程有写权限(如
chmod 755 uploads/) -
basename()防止路径遍历(如../../etc/passwd),但不能替代后缀校验 - 不要拼接用户传来的原始
name直接入库或执行,可能含恶意字符或空格
绕过前端限制?后端必须重验文件类型和大小
HTML 的 accept="image/*" 和 JS 校验全可被绕过。攻击者能用 curl、Postman 或改写 form 直接发任意文件。
关键校验点:
- 用
$_FILES['file']['size']对比ini_get('upload_max_filesize')和业务上限(如不能超 2MB) - 不要信
$_FILES['file']['type'](浏览器提供,可伪造),要用finfo_open(FILEINFO_MIME_TYPE)检测真实 MIME - 检查扩展名需白名单(如
in_array(strtolower(pathinfo($name, PATHINFO_EXTENSION)), ['jpg', 'png', 'gif'])),不能只过滤.php - 上传目录禁止解析 PHP(Apache 需配置
php_flag engine off,Nginx 用location ~ \.php$ { deny all; })
常见报错 Undefined index: file 或 Failed to open stream: No such file
这两个错误基本指向同一个原因:表单没设对 enctype。
HTML 表单必须显式声明:
漏掉 enctype="multipart/form-data",PHP 就不会解析上传字段,$_FILES 为空,$_FILES['file'] 自然报 Undefined index;后续 move_uploaded_file() 因 tmp_name 为空,就会报 No such file。
其他排查点:
- 确认
php.ini中file_uploads = On - 检查
post_max_size≥upload_max_filesize(否则大文件连 POST 都截断) - 上传字段
name大小写要和 PHP 中读取的一致($_FILES['File']≠$_FILES['file'])











