javascript原生态xhr上传多个图片,可预览和修改上传图片为固定尺寸比例,防恶意代码,加后端php处理图片
//前端上传文件
<!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=[];
//处理传过来的$_FIELS文件函数
function previewUpdate(upfiles)
{
//设置预览中添加的div1元素显示在前的顺序
var e=0;
//获取到预览容器preivew
var preview=document.getElementById("preview");
//如果$_FIELS大于0表示有文件过来
if(upfiles.length>0)
{
for(var i=0,len=upfiles.length;i<len;i++)
{
//检测$_FILES的类型是否为图片
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数组中的图片文件
fL.forEach(function(item,index,array){
//创建filereader()异步读取文件
var reader=new FileReader();
//创建预览容器div1和关闭按钮div2
var div1=document.createElement("div");
var div2=document.createElement("div");
//设置他们两的class类名,方便设置样式
div1.className="icon-po";
div2.className="icon-close";
//为div2设置关闭按钮文字X
div2.innerHTML='X';
//设置div1\div2显示的前后顺序
div1.index=div2.index=e;
//如果是多图为后面的图片准备顺序数字
e++;
//异步加载完成后添加图片对象和设置src的url
reader.onload=function(event){
var img=new Image();
//设置图片样式
img.className="pic";
//设置读取到的url
img.src=reader.result;
//图片名字
img.title=item.name;
//将图片添加到div1中
div1.appendChild(img);
//将关闭按钮添加到div1中
div1.appendChild(div2);
//将图片div1小容器,添加到预览容器当中
preview.appendChild(div1);
//点击关闭按钮时,点击谁就关闭div1这个小容器图片,从preview这个大容器中删除这个子节点
div2.onclick=function(){
console.log(item);
div1.remove();
//在数组中将这个图片对象删除掉
fL.splice(index,1);
//console.log(fL);
}
};
//读取每个图片对象
reader.readAsDataURL(item);
});
}else
{
console.log('请选择文件 ');
}
}
//将图片对象上传到服务器文件夹中
function complateUpdate()
{
//创建xhr
var xhr=new createXHR();
//如果fL数组不为空,表示有图片
if(fL.length>0)
{
//创建formData()数据切片
var fd=new FormData();
//循环出数组当中的图片对象,必须用for in来循环,其它的循环会出错
//比如用for(var i=0,len=fL.length...)会有问题,也不能用foreach()
for(var files in fL)
{
//以files序列数字为名,将FL[files]添加到fd切片当中
fd.append(files,fL[files]);
//传输给后端
xhr.open("post","ajaxvalidationimg.php",true);
//发送切片数据
xhr.send(fd);
}
//xhr加载完数据后事件
xhr.onload=function(){
if((xhr.status>=200 && xhr.status<300)|| xhr.status==304)
{
//返回后端的数据
var arrtext=xhr.responseText;
//检测是否是json数据,如果不是则直接弹出信息
//如果是json数据则进行字符串切割,后割[]与[]之间的"\t\n",保存单个[]数据成数组
if(!/[\[\S*]]/.test(arrtext))
{
alert(arrtext);
}else{
var arrtext=arrtext.split("\t\n");
//弹出不需要的空格组
arrtext.pop();
for(var g=0,leng=arrtext.length;g<leng;g++)
{
//解析后端的json数据成对象
var a=JSON.parse(arrtext[g]);
}
//弹出对象中的信息
alert(a.msg);
//调用对象中的数据信息
console.log(a.data);
}
}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>
//后端处理图片文件
<?php
header("Content-Type:text/plain");
//处理上传图片类
class ValidationImg{
//预设图片各种属性
private $files=array(
"name"=>'',
'tmp_name'=>'',
'size'=>'',
"type"=>'',
'error'=>''
);
//最终上传的路径
private $path='images/';
//需要检查的图片类型白名单
private $type=array('image/jpeg','image/jpg','image/png','image/gif');
//图片属性字段
private $fields=array();
//保存上传后图片的各种属性
private $init=array();
//处理时需要的信息和完成后的数据数组
public $message=array(
'msg'=>'',
'data'=>[]
);
//构造函数,获取预设属性的键
public function __construct()
{
$this->fields=array_keys($this->files);
}
//处理上传的资源图片,将各属性保存进init数组中以备后续处理
public function init($source)
{
if(is_array($source) && count($source)>0)
{
foreach($source as $key=>$val)
{
foreach($val as $k=>$v)
{
foreach($this->fields as $default)
{
$this->init[$key][$k]=isset($source[$key][$default]) ? $source[$key][$k] :$source[$key][$default];
}
}
}
}else
{
$this->message['msg']='请上传图片';
return false;
}
return $this->init;
}
//获取资源图片的类型
public function gettype()
{
foreach($this->init as $val)
{
if(in_array($val['type'],$this->type))
{
return true;
}else
{
$this->message['msg']='图片类型不正确';
}
}
}
//创建空白图片以备后用
public function newfiles()
{
foreach($this->init as $key=>$val)
{
$ext[$key]=trim(strrchr($val['name'],'.'),'.');
$newfile[$key]=$this->path.strval(rand()).$ext[$key];
}
return $newfile;
}
//获取资源图片的后缀
public function ext()
{
foreach($this->init as $key=>$val)
{
$ext[$key]=strrchr($val['name'],'.');
}
return $ext;
}
//获取资源图片的临时名字
public function tmpfiles()
{
foreach($this->init as $key=>$val)
{
$tmpfile[$key]=$val['tmp_name'];
}
return $tmpfile;
}
//得到临时文件类型这个更准确,但这函数传有漏洞
public function getimagesize()
{
foreach($this->init as $key=>$val)
{
$type[$key]=@getimagesize($val['tmp_name'])['mime'];
}
return $type;
}
//获取资源图片的大小
public function getsize()
{
foreach($this->init as $key=>$val)
{
if($val['size']<1024*1000)
{
$size[$key]=$val['size'];
}else
{
$this->message['msg']='图片太大,必须小于1M';
return false;
}
}
return $size;
}
//最终处理资源图片形成新的图片,并传数据给前端
public function run()
{
//先处理上传来的资源图片
$f=$this->init($_FILES);
//如果不为空时,且没有错误信息
if(count($f)>0 && $this->message['msg']==='')
{
//资源图片种类都正确时
if($this->gettype()===true && $t= $this->getimagesize())
{
//资源图片大小正确时
if($this->getsize()!==false)
{
if(count($tmp=$this->tmpfiles())>0)
{
//循环临时图片备用
foreach($tmp as $key=>$val)
{
//循环新创建的空白图片备用
foreach($this->newfiles() as $k=>$v)
{
//如果临时图片键=新创建空白图片键
if($key==$k)
{
//将临时图片移动到新空白图片中
if(move_uploaded_file($val,$v))
{
//检测图片类型,选择创建图片资源
switch($t[$key])
{
case 'image/png':
$im=imagecreatefrompng($v);
break;
case 'image/jpeg':
$im=imagecreatefromjpeg($v);
break;
case 'image/jpg':
$im=imagecreatefromjpeg($v);
break;
case 'image/gif':
$im=imagecreatefromgif($v);
break;
case 'default':
$im=false;
break;
}
//如果$im=false表示,imagecratefromjpeg()等函数没能创建出图片
//表示临时文件移动到空白图片中是有问题的,表示用户上传的并不是图片类型
//有可能是修改了后缀上传的代码文件,所以$im为false
if($im==false)
{
$this->message['msg']="只支持png,jpeg,gif,jpg图片格式,请勿上传其它类型文件";
@unlink($v);
}else
{
//新空白图片
$img_path=$this->path.date('YmdHis').strval(rand()).$this->ext()[$key];
//获取资源图片宽度
$srcwidth=imagesx($im);
//获取资源图片高度
$srcheight=imagesy($im);
//设置资源图片与我们设定宽度500值之间的比例保留后2位
$wportion=number_format(($srcwidth/500),2);
//获取资源图片宽/高的比例保留后2位
$portion=number_format($srcwidth/$srcheight,2);
//设置空白目标图片宽,资源图片宽 / 设定宽值比例值
$dstwidth=ceil($srcwidth/$wportion);
//设置空白目标图片高,目标图片宽度 / 资源图片宽高比例值
$dstheight=ceil($dstwidth/$portion);
if($srcwidth>500)
{
//创建空白目标图片宽与高
$dst_image=imagecreatetruecolor($dstwidth,$dstheight);
//将资源图片按比例拷贝进目标空白图片当中
imagecopyresampled($dst_image,$im,0,0,0,0,$dstwidth,$dstheight,$srcwidth,$srcheight);
//将拷贝的目标图片创建进我们设置的新空白地址图片当中,形成新图片,90为保留真色彩值
imagejpeg($dst_image,$img_path,90);
//删除创建的临时资源图片连接
@unlink($v);
//销毁目标图像相关联的所有内存
imagedestroy($dst_image);
//销毁临时图像句柄相关联的所有内存
imagedestroy($im);
$this->message['msg']='上传成功';
array_push($this->message['data'],$img_path);
}else
{
//将临时目标图片移进我们设置的新空白地址图片当中,形成新图片,90为保留真色彩值
imagejpeg($im,$img_path,90);
//删除创建的临时资源图片连接
@unlink($v);
//销毁临时图像句柄相关联的所有内存
imagedestroy($im);
$this->message['msg']='上传成功';
array_push($this->message['data'],$img_path);
}
}
$img=json_encode($this->message);
echo $img;
echo "\t\n";
}else
{
$this->message['msg']="上传出错,请确保图片类型正确";
}
}
}
}
}else
{
echo '临时出错';
}
}
else
{
echo $this->message['msg'];
}
}else
{
echo $this->message['msg'];
}
}else
{
echo $this->message['msg'];
}
}
}
if(isset($_FILES)&& count($_FILES)>0)
{
$f=new ValidationImg();
$v=$f->init($_FILES);
print_r($f->run());
}
//展示效果