(独孤九剑)--文件上传
【一】文件上传
场景:上传博客,文章,图片,人脸扫描等
【二】配置文件
(1)配置项
fille_uploads 开启上传功能on;关闭上传功能off;
post_max_size 系统允许post传参最大值
upload_max_filesize 系统允许上传文件最大值
memory_limit 内存使用限制
建议尺寸:上传文件大小 < upload_max_filesize < post_max_size < memory_limit
(2)拓展
此外,还可以配置临时文件的存储位置。
临时目录中的临时文件有效期默认为脚本周期,即一次请求结束
【三】表单数据类型
表单数据类型有两种:
(1)字符串类型(字节流编码);
(2)文件类型(二进制编码);
*原因解析*表单提交时:浏览器会默认的认为,表单内数据都是字符串类型(即使使用file文件域)。通过在form上增加enctype属性,告知浏览器表单内的数据不仅有字符串,还有文件。从而进行文件提交
上传图片和博客时需要设置上传内容类型为为禁止编码:multipart/form-data
<form> 标签的 enctype 属性规定了在提交表单时要使用哪种内容类型。在表单需要二进制数据时,比如文件内容,请使用 "multipart/form-data"
【四】提交时服务器处理方式
接受浏览器请求时,处理好表单内数据。根据数据类型不同(2种),采用不同处理方式
①字符串类型/字节流,存储在$_POST变量里(内存里)
②文件型数据/二进制,存储在上传临时目录中
【五】错误类型
每个上传的临时文件都有 5个信息:name原始文件名;type类型;tmp_name临时地址;error是否有错及错误类型;size大小
<form action="" method="post" enctype="multipart/form-data"> <input type="file" name="file" id="file" /> <input type="submit" name="submit" value="提交" /> </form> <?php echo "<pre>"; var_dump($_FILES); echo "</pre>"; ?>
提交后输出数组:
array(1) { ["file"]=> array(5) { ["name"]=> string(10) "零食.txt" ["type"]=> string(10) "text/plain" ["tmp_name"]=> string(22) "C:\Windows\phpAD25.tmp" ["error"]=> int(0) ["size"]=> int(443) } }
下面说下错误类型:01234567
0:无错误;
1:文件过大,大于PHP配置upload_max_filesize
2:文件过大,超过表单元素max_file_size
3:上传部分文件
4:没有上传文件
5:上传空文件
6:没有找到临时上传文件(权限控制)
7:临时文件写入失败
【六】案例:上传预览图片
<!DOCTYPE html> <html> <head> <title>PHP测试demo</title> <link rel="stylesheet" href="./demo.css"> <style type="text/css"> #preview{ display: block; width: 100px; height: 100px; border:1px solid red; } </style> </head> <body> <form action="index.php" method="post" enctype="multipart/form-data"> <label for="file">文件名:</label> <input type="file" name="file" id="file" /> <img id="preview" src=<?php echo "./upload/".$_FILES["file"]["name"]?> alt="点击上传图片"/> <br /> <input type="submit" name="submit" value="提交" /> </form> <?php
//name原始文件名;type类型;tmp_name临时地址;error是否有错及错误类型;size大小 if ((($_FILES["file"]["type"] == "image/jpg")|| ($_FILES["file"]["type"] == "image/jpeg") || ($_FILES["file"]["type"] == "image/pjpeg"))){ if ($_FILES["file"]["error"] > 0){ echo "报错: " . $_FILES["file"]["error"] . "<br />"; }else{ echo "文件名: " . $_FILES["file"]["name"] . "<br />"; echo "类型: " . $_FILES["file"]["type"] . "<br />"; echo "大小: " . ($_FILES["file"]["size"] / 1024) . " Kb<br />"; echo "原先保存位置: " . $_FILES["file"]["tmp_name"]."<br>"; //保存 if (file_exists("./upload/" . $_FILES["file"]["name"])){ echo "注意:该文件". $_FILES["file"]["name"] . "已经存在!!!"; }else{ move_uploaded_file($_FILES["file"]["tmp_name"], "./upload/" . $_FILES["file"]["name"]); echo "提交后保存位置: " . "./upload/里的" . $_FILES["file"]["name"]; } } }else{ echo "Invalid file文件类型不符"; }//注意路径,例如../php7/a仍然是保存到php7里,想要保存到php7下的a文件里的话需要 路径为../php7/a/ ?> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script> <script type="text/javascript"> window.FileToSrc = function (file) { if (window.URL) return window.URL.createObjectURL(file); if (window.windcreateObjectURL) return window.createObjectURL(file); if (window.webkitURL) return window.webkitURL.createObjectURL(file); }; $('#file').on('change', function(){ //获取文件列表对象 var fileList = $('#file')[0].files; //创建文件流获取文件地址 var src =FileToSrc(fileList[0]); //设置图片路径 $("#preview").attr("src", src); }); </script> </body> </html>
【七】上传步骤
(1)判断是否有错误码
$_FILES["file"]["error"]读取错误类型
0为无错误,1234567为有错误
if ($_FILES["file"]["error"] > 0){ echo "报错: " . $_FILES["file"]["error"] . "<br />"; }else{ ... }
(2)自定义判断文件大小是否超标
除了php.int中规定的上传最大值外,通常还需要设定业务规定的上传大小限制
例如:新浪微博或QQ空间头像图片规定限制2M,而上传时又可以超过2M
此处的判断文件大小,用于限制实际业务里想要规定的实际文件大小
(3)判断后缀名和mime类型是否符合
黑客可以在图片里植入病毒,在附件里上传病毒,会在网页里插入病毒或者诱惑性图片。所以,我们需要上传文件后缀和mime类型都要进行判断才行
mime类型是多用途互联网邮件扩展类型,设定某种扩展名的文件用一种应用程序来打开的方式类型,当扩展文件名被访问时,浏览器会自动使用指定应用程序来打开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。
在判断后缀和MIME类型的时候,会用到PHP的in_array(判断的值,范围数组)函数,该函数需要用到两个参数
我们用in_array()来判断文件的后缀名和mime类型是否在允许的范围内
(4)生成文件名
文件上传成功后,一般不会让它保存原名。
因为有些人在原名里添加了敏感关键词,会违反国家相关法律
所以采用date()、mt_rand()、unique()生成随机文件名
(5)判断是否是上传文件
(6)移动临时文件到指定位置
<?PHP header("content-type:text/html;charset=utf-8"); if(!empty($_FILES[up_picture][name])){ //判断上传文件是否为空 if($_FILES['up_picture']['error']>0){ //判断文件是否可以上传到服务器 echo "上传错误"; switch($_FILES['up_picture']['error']){ case 1: echo "上传文件大小超出配置文件规定值"; break; case 2: echo "上传文件大小超出表单中约定值"; break; case 3: echo "上传文件不全"; break; case 4: echo "没有上传文件"; break; } }else{ if(!is_dir("./upfile/")){ //判断指定目录是否存在 mkdir("./upfile/"); //创建目录 } $path='./upfile/'.time().strstr($_FILES['up_picture']['name'],'.');//定义文件名称和存储位置 if(is_uploaded_file($_FILES['up_picture']['tmp_name'])){ //判断是否通过HTTP POST上传的 if(!move_uploaded_file($_FILES['up_picture']['tmp_name'],$path)){ //执行上传 echo "上传失败"; }else{ echo "文件".time().$_FILES['up_picture']['name']."上传成功,大小为:".$_FILES['up_picture']['size']; } }else{ echo "上传文件".$_FILES['up_picture']['name']."不合法!"; } } } ?>
【八】步骤解析
(1)判断错误码
if ($_FILES['myfile']['error'] > 0) { switch ($_FILES['file']['error']) { case '1': echo "文件过大"; break; case '2': echo "文件超出指定大小"; break; case '3': echo "只上传了部分文件"; break; case '4': echo "没有上传文件"; break; case '5': echo "上传空文件"; break; case '6': echo "没有找到临时上传文件(权限问题)"; break; case '7': echo "临时文件写入失败"; break; } }else{ echo "无错误"; }
(2)判断文件是否超出大小
实际项目中由于系统硬件的限制,以及存储设备的限制,不可能让用户无限上传文件。所以要对用户上传的文件大小进行限制,能让应用平稳运行
将文件大小上限定义为$MAX_FILE_SIZE,若文件大小大于$MAX_FILE_SIZE则退出上传并错误提示
if ($_FILES['myfile']['error'] > 0) { exit("系统出错0,1,2,3,4,5,6,7"); }else{ $MAX_FILE_SIZE = 1000; if ($_FILES['myfile']['size'] > $MAX_FILE_SIZE) { exit('文件超出指定大小'); } }
(3)判断文件的mime类型是否正确
例如要求上传后缀名为GIF或jpg的文件,当上传不符合要求的文件时返回错误提示
注意:文件类型限制前端即可限制,后端也行
例如:要求只能上传图片
前端限制:选择文件时只会出现图片选项
<input type="file" name="myfile" id="file" accept="image/*"/>
后端限制:选择文件时随意,上传时判断进行限制
$allow = array('jpg','gif');//规定允许的类型 $myImg = explode('.', $_FILES['myfile']['name']);//explode()将字符串打散为数组 $suffix = array_pop($myImg);//array_pop删除数组末尾元素 if (!in_array($suffix, $allow)) { exit("文件后缀名不符合"); }else{ echo "开始上传"; }
或者用数组简单处理
$allow =array("image/jpg","image/jpeg","image/pjqeg","img/gif"); if (!in_array($_FILES['myfile']['type'], $allow)) { exit(文件格式不正确,请检查); }else{ echo "开始上传"; }
(4)生成指定文件路径和文件名
为了避免文件名重复造成的错误,可以按照一定格式生成随机文件名
$path = "upload/imgs/"; $name = date('y').date('m').date('d').date('h').date('i').date('s').rand(0,9).'.'.'. $suffic';
//date('y').date('m').date('d').date('h').date('i').date('s')按照日期生成随机数
//rand()生成指定范围的随机数
(5)判断是否上传文件
if (is_uploaded_file($_FILES['myfile']['tmp_name'])) { ...
}
(6)移动文件到指定位置
使用move_uploaded_file(filename, destination)将文件移动到指定位置保存
if(is_uploaded_file($_FILES['myfile']['tmp_name'])){ if (move_uploaded_file($_FILES['myfile']['tmp_name'], $path. $name)) { echo "上传成功"; }else{ echo "上传失败"; } }else{ echo "不是上传文件"; }
【九】多文件上传
(1)可以使用$_FILES来接受文件信息,打印并查看数组
<form action="" method="post" enctype="multipart/form-data"> <input type="file" name="file[]"/><hr> <input type="file" name="file[]"/><hr> <input type="submit" name="submit" value="提交" /> </form> <?php echo "<pre>"; var_dump($_FILES['file']); echo "</pre>"; ?>
(2)多文件时,从打印数据可以看出文件信息被存到数组里。这时需要用到循环来读取单个文件的信息。
$array = $_FILES['file']['name']; $length = count($array); for($i=0;$i<$length;$i++){ echo "文件名为".$array[$i]."<br>"; echo "原先保存位置: " . $_FILES["file"]["tmp_name"][$i]."<br>"; move_uploaded_file($_FILES["file"]["tmp_name"][$i],"./upload/" . $array[$i]); echo "提交后保存位置: " . "./upload/里的" . $array[$i]."<br>"; }
注意:临时文件名后需加[$i]来进行区分辨别
【十】文件上传进度处理
在PHP的5.4版本之前,需要安装额外的扩展才能监控到文件上传进度。
5.4版本之后,引入了session.upload_progress的新特性,只需要在php.int里开启配置,即可通过session监控文件上传进度
php.int里配置项:
session.upoad_progress.enabled 是否启用上传进度报告(默认开启,1开启,0关闭)
session.upoad_progress.cleanup 是否在上传完成后及时删除进度数据(默认开启,推荐开启)
session.upload_progress.prefix[=upload_progress_] 进度数据存储位置
session.upload_progress.freq[=1%] 更新进度的频率(已经处理的字节数),也支持百分比表示%
session.upload_progress.min_freq[=1.0] 更新进度的时间间隔(秒数)
开启配置后,可以通过session,来记录一个完整的文件上传进度
代码推荐网址:http://www.jb51.net/article/56305.htm
另外,UI使用一些插件上传文件,就可以获取上传进度,不用这么麻烦,例如 Webuploader
(拓展)
①MIME (Multipurpose Internet Mail Extensions) 多用途Internet邮件扩展,是描述消息内容类型的因特网标准