(独孤九剑)--文件上传

【一】文件上传

场景:上传博客,文章,图片,人脸扫描等

【二】配置文件

(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:WindowsphpAD25.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邮件扩展,是描述消息内容类型的因特网标准

原文地址:https://www.cnblogs.com/fightjianxian/p/8594710.html