javascript前端xhr利用FormData()和FileReader()函数上传图片,及后端php接收处理FormData()上传数据只能收到1个数据的处理办法和防止上传漏洞办法

//前端 的检查类型为简单检查,因为渗透会停掉前端的javascript代码,所以在后端进行检测和替换才是重点

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="UTF-8"></html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html;charset=UTF-8;"/>
       
        <title>做上传文件测试用</title>
       <script type="text/javascript" src="common.js"></script>

       <style type="text/css">
        .button
        {
            margin-right: 20px;
        }
        #preview 
        {
            display: flex;
            flex-wrap: wrap;
            width: 800px;
            padding: 10px;
            justify-content: start;
        }
        .icon-po 
        {
            overflow: hidden;
            position: relative;
            width: 300px;
            height: 300px;
            margin-right: 20px;
            margin-top: 20px;
        }

        .icon-close 
        {
            position: absolute;
            right: 5%;
            top: 5%;
            width: 30px;
            border-radius: 50%;
            background-color: red;
            color: #fff;
            font-size: 12px;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        .pic 
        {
            width:300px;
            height:300px;
        }
        </style>

    </head>
    <body>
    选择文件(可多选):<input type="file" id="f1" multiple/>
    <button type="button" class="button" id="btn-submit">预览图片</button>
    <button type="button" id="complate">上传图片</button>
    <div id="preview"></div>
    <br/>
    <script type="text/javascript">
        var fL=[];
        //创建获取图片以及预览图片
        function previewUpdate(upfiles)
        {
            var e=0;
            
            var preview=document.getElementById("preview");
            if(upfiles.length>0)
            {
                for(var i=0,len=upfiles.length;i<len;i++)
                {
                   //简单检测是否为图片类型
                    if(!/image\/(jpe?g|png|gif)/i.test(upfiles[i].type))
                    {
                        alert(upfiles[i].name+'不是图片');
                    }else
                    {
                    //上传图片不能大于1M
                        if(upfiles[i].size<1024*1000)
                        {
                        //最后都压入fL数组内以供后面程序调用
                            fL.push(upfiles[i]);
                            
                        }else
                        {
                            alert('图片过大,请上传小于1M的图片');
                        }
                        
                    }
                    /*这里直接输入是为了后端测试用,可以删除掉
                   //直接给到数组
                   fL.push(upfiles[i]);
                   */
                }
              //循遍数组里面的图片对象
                fL.forEach(function(item,index,array){
                    //创建FileReader()读取图片对象
                    var reader=new FileReader();
                    //为预览图片创建容器和关闭按钮,div1为装图片的容器,div2为关闭单个div1容器
                    var div1=document.createElement("div");
                        var div2=document.createElement("div");
                        div1.className="icon-po";
                        div2.className="icon-close";
                        div2.innerHTML='X';
                       //为每个容器和关闭按钮设置显示前后顺序
                        div1.index=div2.index=e;
                        e++;
                      //reader.onload为读取完成后创建图片对象,添加到div1容器中  
                    reader.onload=function(event){
                        var img=new Image();
                        img.className="pic";
                        //获得图片的URL连接,方便预览
                        img.src=reader.result;
                        img.title=item.name;
                        div1.appendChild(img);
                        div1.appendChild(div2);
                        preview.appendChild(div1);
                        //点击关闭按钮div2,删除div1子节
                        div2.onclick=function(){
                            console.log(item);
                            div1.remove();
                            //同时删除div1容器图片对象在fL数组中相应的值
                            fL.splice(index,1);
                            //console.log(fL);
                            
                        }



                    };
                    //读取数组中的每一项图片对象
                    reader.readAsDataURL(item);
                });   
            }else
            {
                console.log('请选择文件 ');
            }    
            
        }
	//上传图片函数
        function complateUpdate()
        {
        //创建xhr对象
            var xhr=new createXHR();
            
            //判断数组是否不为空
            if(fL.length>0)
            {
                //创建formdata()
               var fd=new FormData();
               //注意:这里必须用for in来将fL中的图片对象添加到fd当中,相当于是循环取出对象
               //不能用for(var i=0,len=fL.length;i<len;i++)循环
               //或是fl.forEach()这些,用这些方法传过去的图片,后端都是只能接收到最后一个
               
               for(var files in fL)
               {
               //files表示的是fL中的键,一般就是数字的自然数,fL[files]就是数组中的图片对象了
                fd.append(files,fL[files]);
                xhr.open("post","ajax.php",true);
                xhr.send(fd);
                
               }
               
              
                //xhr.onload加载完成时触发的事件是回调 后端传来的数据
                xhr.onload=function(){
                    
                        if((xhr.status>=200 && xhr.status<300)|| xhr.status==304)
                        {
                            alert(xhr.responseText);
                        }else
                        {
                            console.log("接收数据发生错误");
                        }
                    
                };
  
            }else
            {
                alert('无图片');
            }
        }

        var button=document.getElementById('btn-submit');
        var upfiles=document.getElementById("f1");
        var upbutton=document.getElementById("complate");
        //选择上传图片后就显示预览
        upfiles.onchange=function(){
            previewUpdate(upfiles.files);
 
        }
        //点击预览按钮可变换显示和隐藏预览图片
        button.onclick=function(){
            var preview=document.getElementById("preview");
            var allStyle=document.defaultView.getComputedStyle(preview,null);
            if(allStyle.display!="none"){
                preview.style.display="none";
            }else
            {
                preview.style.display="flex";
            }
        };
        //上传按钮提交上传的图片
        upbutton.onclick=function(){
            complateUpdate();
            //数组清空,防止重复提交
            fL=[];
            var preview=document.getElementById("preview");
            //清空预览区已经上传的图片
            preview.innerHTML='';
          
        };

        
    </script>
    </body>   
</html> 

//后端文件,就是在 xhr.open(“post”,“ajax.php”,true);这句里面的ajax.php

<?php
header('Content-type:text/plain;enctype:multipart/form-data');
if($files=$_FILES)
{
    //设置路径
    $path="images/";
    if(!file_exists($path))
    {
        mkdir("$path",0700);
    }
    //将多张图片循环出来
   for($i=0;$i<count($files);$i++)
   {
   		
   		//获取文件名后缀
        $pos[$i]=trim(strrchr($files[$i]['name'],"."),".");
        //获取文件类型
        $type[$i]=$files[$i]['type'];
        //获取文件临时名字
        $tmp_name[$i]=$files[$i]['tmp_name'];
        //创建一个完整的随机名字的新图片文件
        $newfile[$i]=$path.strval(rand()).".$pos[$i]";
        //用getimagesize()获取每个图片的类型,必须是临时文件的类型,如果是用$files[$i]['name']来获取类型会出错
        //getimagesize()说有漏洞问题,最好用@隐藏下出错的错误显示,
        //为什么这里又要获取图片的类型?因为$files[$i]['type']获取到图片的类型并不准确
        $typemime[$i]=@getimagesize($files[$i]['tmp_name']);
        
        //array_diff比较两个数组值的差集,就是比较参数1的值,是不是在参数2中都有,如果有一项不是,则会输出这个值,这函数也可以比较对象
        $result=array_diff($pos,array('png','jpeg','jpg','gif'));
        $rtype=array_diff($type,array('image/jpeg','image/png','image/jpg','image/gif'));
        
        if(count($result)===0 && count($rtype)===0)
        {
        	//图片是否小于1M
            if($files[$i]['size']<1024*1000){
            	//将临时文件移动到新创建的图片中
                if(move_uploaded_file($tmp_name[$i],$newfile[$i]))
                {
                    //这里就用了getimagesize()获取到的类型['mime']来选择用那块创建新的图片
                    //用imagecreatefromjpreg/png/gif可以有效清除图片中的恶意代码,也可以判断是不是图片
                    //如果不是图片是创建不了新图片的,目前这是最有效的防恶意代码的方法,其它办法:如去写清理图片中的代码总有漏洞
                    switch($typemime[$i]['mime'])
                    {
                        case 'image/jpeg': 
                            $im=imagecreatefromjpeg($newfile[$i]);
                            break;
                        case 'image/png': 
                            $im=imagecreatefrompng($newfile[$i]);
                            break;
                        case 'image/gif': 
                            $im=imagecreatefromgif($newfile[$i]);
                            break; 
                        case 'image/jpg': 
                            $im=imagecreatefromjpeg($newfile[$i]);
                            break;
                        default:
                            $im=false;
                            break;
                    }
                    
                    if($im==false)
                    {
                        echo "只支持png,jpeg,gif,jpg图片格式,请勿上传其它类型文件";
                        @unlink($newfile[$i]);
                        
                    }
                    else
                    {
                        //再重新创建一个新的空白图片
                        $img_path[$i]=$path.date('YmdHis').strval(rand()).".$pos[$i]";
                        //将上面创建的$im图像对象句柄写入新的空白图片中
                        imagejpeg($im,$img_path[$i]);
                        @unlink($newfile[$i]);
                        //清理句柄释放内存
                        imagedestroy($im);
                        echo $files[$i]['name'].'上传成功';
                    }
                }else
                {
                    echo '上传出错,确保图片格式正确';
                }
            }else
            {
                echo '图片过大,必须小于1M';
            }
            
           
        }else
        {
            echo '上传失败!文件类型不正确,请确保正确的图片格式。';
        }
        
   }
}else
{
    echo '未获取到数据';
}

?>