PHP实现上传文件并自动创建递归目录的完整代码
在开发用户头像或文件存储功能时,常常需要根据用户ID动态生成多层级的存储目录(如 D/AV/IC/...)。如果目标目录不存在且未提前创建,上传操作会失败。本文将通过一段PHP代码演示如何结合路径解析、递归目录创建与标准文件上传接口,实现“路径不存在则自动创建,存在则直接移动”的自动化流程。
核心逻辑与结构
该方案主要包含三个部分:路径生成器、递归目录创建函数以及主上传控制逻辑。整体流程为:根据输入参数计算文件应存放的物理路径 → 提取路径中的目录部分 → 调用递归函数确保所有上级目录存在 → 检查临时文件上传状态 → 将文件从临时位置移动到最终目标路径。
准备条件与环境设置
在使用此代码前,请确保运行环境满足以下条件:
- PHP版本:至少支持
>=5.0的标准环境,现代环境建议使用 PHP 7.4 或 PHP 8.x 以获得更好的性能和安全特性。 - 目录权限:Web服务器进程(如 Nginx 的
www-data或 Apache 的apache)必须对目标根目录(例如D:/avic/discuz/uc_server/data/avatar/)具有写入权限。若权限不足,mkdir操作会静默失败。 - POST大小限制:确保
php.ini中的upload_max_filesize和post_max_size设置了允许的文件上限,否则$_FILES可能无法获取数据。
第1步:构建目标路径逻辑
为了避免单一目录下文件过多导致读写瓶颈,通常将文件分散存储到子文件夹中。本例通过 get_avatar() 函数模拟了这一过程。
目的:将用户ID转化为稳定的多级目录结构,并确保文件名符合规范。
代码说明:
- 安全截断:使用
abs(intval($uid))确保UID为正整数,防止恶意构造非法字符。 - 层级分割:将9位数字的UID分割为3-2-2-2的四段,分别作为一级、二级、三级子目录名。
- 后缀追加:根据类型添加
_real后缀,并拼接尺寸标识(如_middle)和扩展名。
//返回图片要存储的路径
function get_avatar($uid, $size = 'middle', $type = '') {
$size = in_array($size, array('big', 'middle', 'small')) ? $size : 'middle';
$uid = abs(intval($uid));
$uid = sprintf("%09d", $uid);
$dir1 = substr($uid, 0, 3);
$dir2 = substr($uid, 3, 2);
$dir3 = substr($uid, 5, 2);
$typeadd = $type == 'real' ? '_real' : '';
return $dir1.'/'.$dir2.'/'.$dir3.'/'.substr($uid, -2).$typeadd."_avatar_$size.jpg";
}
第2步:实现递归目录创建
直接使用 mkdir() 仅能创建最深层目录,若父目录不存在则会报错。因此需要使用递归算法逐层向上检查。
入口:接收待创建的绝对或相对目录路径 $dir。
动作:
- 首先判断
is_dir($dir),若已存在则直接返回true,避免重复创建。 - 若不存在,先递归调用自身创建其父目录
dirname($dir)。 - 父目录确认存在后,调用
mkdir($dir, 0777)创建当前层级。 - 注意:生产环境中不建议使用
0777这种完全开放的权限,建议根据需求设置为0755以防止其他用户随意修改。
//递归创建目录,若传的$dir不是绝对路径,则会和运行此方法的目录同级
function mkdirs($dir){
if(!is_dir($dir)){
if(!mkdirs(dirname($dir))){
return false;
}
if(!mkdir($dir,0777)){
return false;
}
}
return true;
}
第3步:执行上传与控制流
这是整个逻辑的主控部分,负责协调目录创建与文件转移。
重要参数:依赖全局变量 $_REQUEST['uid'] 和 $_FILES['pic']。
操作步骤:
- 组合路径:将基础存储路径与前文生成的相对路径拼接,得到物理路径
$avatar。 - 提取目录:使用
dirname($avatar)获取文件所属的文件夹路径。 - 创建目录:调用
mkdirs($dir)。只有在此步骤成功(返回true)后,才继续执行文件移动,以避免因目录缺失导致的移动失败。 - 验证错误码:检查
$_FILES["pic"]["error"]。标准HTTP上传中,错误码为0表示无错误;虽然原文逻辑写的是>= 0,但这通常意味着只要没有产生致命的系统级错误(如未选择文件时的特定状态),就尝试移动。更严谨的做法是严格判断== UPLOAD_ERR_OK。 - 移动文件:调用
move_uploaded_file($_FILES['pic']['tmp_name'], $avatar)。该函数会确保文件是通过HTTP POST上传的,防止跨站请求伪造攻击。
$uid=$_REQUEST['uid'];
//假设 $size, $type, $uid 已有合理赋值
$avatar = 'D:/avic/discuz/uc_server/data/avatar/'.get_avatar($uid, $size, $type);
$dir=dirname($avatar);
$errorcode=1;
$errormsg="";
//创建目录成功后移动临时文件
if(mkdirs($dir)){
// 建议在实际使用中明确判断 error == 0
if(isset($_FILES["pic"]) && $_FILES["pic"]["error"] == 0){
if(move_uploaded_file($_FILES['pic']['tmp_name'],$avatar)){
$errorcode=1;
}else{
$errorcode=0;
$errormsg="文件移动失败";
}
}else{
$errorcode=0;
$errormsg="上传文件出错: ".$_FILES['pic']['error'];
}
}else{
$errorcode=0;
$errormsg="无法创建目录";
}
$back=array("errorcode"=>$errorcode,'errormsg'=>$errormsg);
header('Content-Type: application/json');
echo json_encode($back);
完成判断与排错指南
执行完毕后,API将返回JSON格式结果:{"errorcode":1,"errormsg":""} 表示成功。errorcode 为0表示失败,errormsg 会提供具体原因。
若遇到失败,请按以下顺序排查:
- 权限拒绝:检查Web服务器用户对
D:/avic/discuz/uc_server/data/avatar/是否拥有写权限。可通过chmod或Windows属性设置调整。 - 磁盘空间:确认目标分区剩余空间充足。
- 文件被占用:如果在Windows环境下,某些杀毒软件可能会锁定刚上传的文件导致
move_uploaded_file失败,可稍后重试或添加延迟。
以上就是PHP上传文件并自动创建递归目录的详细内容,更多关于PHP文件操作与目录管理的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









