文件上传其实很简单,通过form表单就可以实现。但form表单上传文件只是把文件放到服务器的临时目录,它还需要我们把上传到临时目录的文件转放到服务器的指定目录。简单的说:把文件从临时目录移动到指定目录。这里心月it博客要分享的是通过 move_uploaded_file() 方法来实现文件上传。
move_upload_file(string $filename , string $destination ):将上传的文件移动到新位置。这个函数会检查并确保由 filename 指定的文件是合法的上传文件(即通过 PHP 的 HTTP POST 上传机制所上传的)。如果文件合法,则将其移动为由 destination 指定的文件。为保证服务器和数据的安全性,这种检查是非常有必要的。
文件上传的步骤:
1、form表单:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>文件上传</title> </head> <body> <div> <form action="" method="post" enctype="multipart/form-data"> 选择上传文件:<input type="file" name="upFile[]" multiple="multiple"> <input type="submit" value="上传"> </form> </div> </body> </html>
input中加上multiple="multiple"可以实现同时选中多个文件上传,但‘name’值后一定要加‘[]’,否则尽管可以同时选择多个文件,但只有最后一个是有效的。
使用 multiple="multiple" 我们可以上传任意数量的文件,只要不超过服务器一次允许上传的文件数量限制以及这些文件的总大小不超过服务器的 upload_max_filesize 值即可。
当然,也可以不用multiple="multiple",使用多个input一个一个文件选择然后上传,但依然要注意‘name’值中的‘[]’不能少。
<form action="" method="post" enctype="multipart/form-data"> 选择上传文件: <input type="file" name="upFile[]"> <input type="file" name="upFile[]"> <input type="file" name="upFile[]"> <input type="file" name="upFile[]"> <input type="file" name="upFile[]"> <input type="submit" value="上传"> </form>
2、后端服务器处理上传文件
后端处理主要验证上传文件是否合法,是否为允许上传类型。
在验证的过程中,只要有上一步符合要求才继续往下处理,否则立即中止,并返回错误提示信息。
①验证文件是否是通过 HTTP POST 上传的
is_uploaded_file():对上传文件 $_FILES 中 tmp_name 验证,如果是 HTTP POST 上传的返回 true 否则返回 false。
//检验是否通过 POST 上传 function checkPost($tmpName,&$mes){ if (is_uploaded_file($tmpName)) { return true; }else{ $mes = '非法操作'; return false; } }
②是否图片验证
很多情况我们只允许上传图片,但这个对稍微懂点程序的人来说都不是问题,把病毒或者其他非图片文件变成图片文件是轻而易举的事,因此,需要验证是否是真实图片,
getimagesize($tmpName):如果是真实的图片,改函数会返回图片的数组信息,否则返回false。
这里的$tmpName就是上传文件的临时路径‘tmp_name’。
//是否为真实图片判断 function trueImg($tmpName,&$mes){ if(is_array(@getimagesize($tmpName))){ return true; }else{ $mes = '非法文件'; return false; } }
③验证文件上传的错误号
在文件上传的$_FILES中有‘error’错误号,只有它的值为‘0’,上传才是正常的。
error的值对应的错误:
0: 上传成功 1: 上传文件超出php配置max_upload_filesize限制 2: 上传文件超出html表单限制 3: 文件只有部分被上传 4: 没有上传文件 6: 没有找不到临时文件夹 7: 文件写入失败(可能是文件权限不足) 8: php文件上传扩展file没有打开
如果错误信息是给用户看,那么没有必要给的这么详细,给出个大概即可:
//上传文件错误号判断 function checkErroe($error,&$mes){ if ($error>0) { switch ($error) { case 1: $ms = '上传失败,文件过大'; break; case 2: $ms = '上传失败,文件过大'; break; case 3: $ms = '上传失败,部分上传失败'; break; case 4: $ms = '上传失败,文件没有被上传'; break; case 6: $ms = '上传失败,找不到临时文件夹'; break; case 7: $ms = '上传失败,写入失败'; break; case 8: $ms = '上传失败,系统错误'; break; } $mes = $ms; return false; }else{ return true; } }
④上传文件扩展名验证
防止用户上传的是我们不允许上传的文件类型,在$_FILES中有‘name’文件文件名(包含文件扩展名),我们要验证的就是这个‘name’
//判断文件是否为运行上传类型 function checkExt($fileName, &$mes, $ext = ['jpeg', 'gif', 'jpg',' gif', 'png']){ if (!in_array(pathinfo($fileName)['extension'], $ext)) { $mes = '文件类型错误'; return false; }else{ return true; } }
⑤验证文件大小
虽然服务器内可以设置允许上传文件大小,但在开发过程中有些地方可能会需要一个较大的值,但允许上传文件的大小比这个值小,因此需要过滤过大的上传文件。上传文件信息$_FILES中的‘size’就是上传文件的大小,正是我们要验证的内容。
//判断文件大小 function checkSize($size, &$mes, $maxSize=2097152){ if ($size>$maxSize) { $mes = '上传文件过大'; return false; }else{ return true; } }
⑥检验指定上传文件存放路径是否存在,然后移动上传文件到指定目录
该检验的信息都通过就可以移动上传文件到指定目录了,但在移动之前还是要判断下目录是否存在,如果不存在则需要创建
指定目录存在后就可以把上传文件移动到指定目录了。
在移动文件的时候有个问题需要注意,如果网页编码是UTF-8,且上传文件的文件名也是中文,在移动之前需要把文件名的编码转为gb2312,否则移动文件失败。
//单个文件移动 function moveFile($file,&$mes='',$imgFlg = true, $destination='uploadfile/'){ if ($imgFlg) { if(!trueImg($file['tmp_name'],$mes)){ return false; } } if (checkPost($file['tmp_name'],$mes) && checkErroe($file['error'],$mes) && checkExt($file['name'],$mes) && checkSize($file['size'],$mes)) { //所有验证通过,移动文件到指定目录 //检验指定目录是否存在,如果不存在则创建目录 if (!is_dir($destination)) { mkdir($destination,'0777',true); } //把上传文件从临时目录移动到指定目录 $fileName = iconv('utf-8', 'gb2312', $file['name']); if (move_uploaded_file($file['tmp_name'], $destination.$fileName)) { $mes = $file['name'].'上传成功'; }else{ $mes = $file['name'].'上传失败'; } return $mes; }else{ return false; } }
⑦如果我们是通过multiple="multiple"上传多文件,为了方便后续操作,$_FILES需要做处理,让每个上传文件的所有信息都存放在同一个数组。
//上传文件信息整理 function getFilesInfo($filesInfo){ if (is_array($filesInfo['name'])) { $fileArr = []; $i=0; $filesNum = count($filesInfo['name']); for($i=0;$i<$filesNum;$i++){ $fileArr[$i]=[ 'name' => $filesInfo['name'][$i], 'type' => $filesInfo['type'][$i], 'tmp_name' => $filesInfo['tmp_name'][$i], 'error' => $filesInfo['error'][$i], 'size' => $filesInfo['size'][$i] ]; } return $fileArr; }else{ return false; } }
上传文件信息整理好之后,通过遍历的方式用单文件上传的方法将文件逐一移动到指定目录,也就是⑥中的moveFile()方法。
结语:
1、在信息校验中引用了mes变量,主要是为了确保错误提示信息与真实的错误一致。
2、这里分享的是通过面向过程实现的文件上传,函数传参比较多,如果用面向对象的话就不需要这么多的参数,除了单文件上传验证必要的参数外,其他的都可以放大成员属性中。比如:默认允许上传的单文件最大大小,允许上传文件的扩展类型,是否验证图片的真实性,上传文件保存目录,整理后的上传文件信息,文件上传结果信息,以及上传文件input的name值等。如果这些值有变,只需在实例化类时传入新的值即可。
完整的文件上传实现过程(单文件多文件都实用)
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>文件上传</title> </head> <body> <div> <form action="" method="post" enctype="multipart/form-data"> 选择上传文件:<input type="file" name="upFile[]" multiple="multiple"> <input type="submit" value="上传"> </form> </div> </body> </html> <?php if (isset($_FILES['upFile']['tmp_name']) && !empty($_FILES['upFile']['tmp_name'])) { $filesInfo = $_FILES['upFile']; $files = getFilesInfo($filesInfo); var_dump(uploadFile($files)); }else{ echo "请选择上传文件"; } //文件批量移动 function uploadFile($files){ if (!empty($files)) { $resArr =[]; $i=0; foreach ($files as $value) { $mes = ''; moveFile($value,$mes); $resArr[]=$mes; $i++; } return $resArr; }else{ return false; } } //单个文件移动 function moveFile($file,&$mes='',$imgFlg = true, $destination='uploadfile/'){ if ($imgFlg) { if(!trueImg($file['tmp_name'],$mes)){ return false; } } if (checkPost($file['tmp_name'],$mes) && checkErroe($file['error'],$mes) && checkExt($file['name'],$mes) && checkSize($file['size'],$mes)) { //所有验证通过,移动文件到指定目录 //检验指定目录是否存在,如果不存在则创建目录 if (!is_dir($destination)) { mkdir($destination,'0777',true); } $fileName = iconv('utf-8', 'gb2312', $file['name']); if (move_uploaded_file($file['tmp_name'], $destination.$fileName)) { $mes = $file['name'].'上传成功'; }else{ $mes = $file['name'].'上传失败'; } return $mes; }else{ return false; } } //判断文件大小 function checkSize($size, &$mes, $maxSize=2097152){ if ($size>$maxSize) { $mes = '上传文件过大'; return false; }else{ return true; } } //是否为真实图片判断 function trueImg($tmpName,&$mes){ if(is_array(@getimagesize($tmpName))){ return true; }else{ $mes = '非法文件'; return false; } } //判断文件是否为运行上传类型 function checkExt($fileName, &$mes, $ext = ['jpeg', 'gif', 'jpg',' gif', 'png']){ if (!in_array(pathinfo($fileName)['extension'], $ext)) { $mes = '文件类型错误'; return false; }else{ return true; } } //上传文件错误号判断 function checkErroe($error,&$mes){ if ($error>0) { switch ($error) { case 1: $ms = '上传失败,文件过大'; break; case 2: $ms = '上传失败,文件过大'; break; case 3: $ms = '上传失败,部分上传失败'; break; case 4: $ms = '上传失败,文件没有被上传'; break; case 6: $ms = '上传失败,找不到临时文件夹'; break; case 7: $ms = '上传失败,写入失败'; break; case 8: $ms = '上传失败,系统错误'; break; } $mes = $ms; return false; }else{ return true; } } //检验是否通过 POST 上传 function checkPost($tmpName,&$mes){ if (is_uploaded_file($tmpName)) { return true; }else{ $mes = '非法操作'; return false; } } //上传文件信息整理 function getFilesInfo($filesInfo){ if (is_array($filesInfo['name'])) { $fileArr = []; $i=0; $filesNum = count($filesInfo['name']); for($i=0;$i<$filesNum;$i++){ $fileArr[$i]=[ 'name' => $filesInfo['name'][$i], 'type' => $filesInfo['type'][$i], 'tmp_name' => $filesInfo['tmp_name'][$i], 'error' => $filesInfo['error'][$i], 'size' => $filesInfo['size'][$i] ]; } return $fileArr; }else{ return false; } }