php上传文件指定保存路径需用绝对路径并确保目录存在且web服务器用户有写权限,否则move_uploaded_file()静默失败;须校验$_files’file’、临时文件存在性、目标路径合法性及selinux/apparmor策略;文件名须过滤路径、白名单校验扩展名并重命名;upload_max_filesize和post_max_size修改后需重启服务生效。

PHP上传文件时如何指定保存路径
必须先确认目标目录存在且Web服务器进程(如www-data、apache或nginx用户)有写入权限,否则move_uploaded_file()会静默失败。路径不能直接用相对路径如"uploads/",而应使用绝对路径,推荐通过__DIR__或dirname(__FILE__) 拼接。
- 正确写法:
$target_dir = __DIR__ . '/uploads/'; - 错误写法:
$target_dir = 'uploads/';(可能因当前工作目录不确定导致路径错乱) - 创建目录前务必检查:
if (!is_dir($target_dir) && !mkdir($target_dir, 0755, true)) { die('目录创建失败'); }
为什么move_uploaded_file()总返回false
这是最常见上传失败表现,根本原因几乎都出在目标路径或权限上,而不是$_FILES数据本身。它不会抛出异常,只返回布尔值,所以必须显式判断并排查。
- 检查
$_FILES['file']['Error']是否为UPLOAD_ERR_OK(0),非0说明上传阶段已出错(如超限、临时文件丢失) - 确认
$_FILES['file']['tmp_name']是真实存在的临时文件路径(可用file_exists()验证) - 目标路径末尾必须带斜杠,且父目录可写;若目标是
/var/www/html/uploads/test.jpg,则/var/www/html/uploads/必须存在且可写 - SELinux或AppArmor启用时也可能拦截写入,需检查系统日志(
auditctl -w /var/log/audit.log)
如何安全地构造上传后的文件名
绝不能直接使用$_FILES['file']['name'],它完全由客户端控制,可能含路径遍历(../../etc/passwd)、空字节、非法字符或恶意扩展名。
- 剥离路径信息:
$original_name = basename($_FILES['file']['name']); - 过滤扩展名:
$ext = pathinfo($original_name, PATHINFO_EXTENSION);,再白名单校验(如in_array(strtolower($ext), ['jpg', 'png', 'pdf'])) - 重命名文件:
$new_name = uniqid() . '.' . $ext;或结合hash_file('sha256', $_FILES['file']['tmp_name'])生成唯一名 - 禁止执行:上传目录应禁用PHP解析(Apache中用
.htaccess加php_flag engine off,Nginx中配置location ~ .php$ { deny all; })
upload_max_filesize和post_max_size不生效怎么办
这两个是PHP核心配置项,修改后必须重启PHP-FPM或Web服务器进程才生效,仅重载配置无效。且post_max_size必须大于等于upload_max_filesize,否则表单提交直接被截断。
立即学习“PHP免费学习笔记(深入)”;
- 查看当前值:
php -i | grep -E 'upload_max_filesize|post_max_size'或在脚本中用ini_get('upload_max_filesize') - 修改位置:优先改
php.ini,虚拟主机环境可能需在.user.ini或php_admin_value中设置 - 注意单位:
2M表示2兆字节,不是2MB(二者等价),但2m会被忽略,必须大写M - 如果用cPanel或Plesk,部分托管环境锁定这些参数,只能联系服务商或改用分片上传绕过限制
实际部署时最容易被忽略的是上传目录的**父级目录权限继承问题**——比如/var/www/html/uploads可写,但/var/www/html属主是root且无组写权限,会导致mkdir(..., true)创建子目录失败。这种问题不会报错,只会卡在move_uploaded_file()返回false。