Ajax学习(3)

由于js中无法访问本地文件,因此用ajax上传文件无法实现,在早些时候的上传文件,都通过iframe或swf等方式来上传

而HTML5出现之后,可以实现ajax上传文件。

1、iframe下载文件

要达到的要求是,无刷新页面的上传文件,因此在提交数据表单的时候,将表单的target指向当前页面的iframe即可达到目的

前台测试代码 iframe_file.html

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<script type="text/javascript" src="http://libs.baidu.com/jquery/1.7.2/jquery.min.js"></script>	
	<script type="text/javascript">
		/*
		分析:
		1、捕捉表单提交的动作
		2、创建iframe
		3、把表单的target修改指向该iframe
		4、去掉该iframe
		 */
		function ajaxUp() {
			var ifname = Math.random();
			$('<iframe name="'+ifname+'" width="0" height="0" frameBorder="0"></iframe>').appendTo($("body"));
			$('form:first').attr('target',ifname);
			$('#progress').html('<img src="jindutiao.jpg"/>');
			// return false;
		}
	</script>
	<style type="text/css">
	p{
		border:1px solid gray;
	}
	</style>
</head>
<body>
	<h1>iframe模拟ajax文件上传效果</h1>
	<div id="progress"></div>
	<form method="post" action="iframe_file.php" enctype="multipart/form-data" οnsubmit="return ajaxUp()">
		<p><input type="file" name="pic"></input></p>
		<p><input type="submit" name="提交"></input></p>
	</form>
</body>
</html>

后台测试代码iframe_file.php

<?php 
sleep(3);
if(empty($_FILES)){
	exit("no file");
}
$error = $_FILES['pic']['error']==0 ? 'succ':'fail';
// echo $error;
if($error == 0){
	echo "<script>parent.document.getElementById('progress').innerHTML='$error'</script>";
}
?>

2、FormData的使用

FormData是HTML5中的新特性,因此IE的老版本不支持,所以不考虑在内

FormData作为对象可以直接封装表单的dom对象

前台测试代码formData.html

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>formData</title>
	<script type="text/javascript">
		/*formData
		打包表单数据
		*/
	function mySend(argument) {
		// body...
		/*表单数据*/
		var fm = document.getElementById("myForm");
		var fd = new FormData(fm);
		/*直接添加数据*/
		var fd2 = new FormData();
		fd2.append('username2',"ruiy");
		//xhr对象建立、链接、发送
		var xhr = new XMLHttpRequest();
		xhr.open("POST","formData.php",true);
		xhr.onreadystatechange = function() {
			if(this.readyState==4 && this.status==200){
				document.getElementById("debug").innerHTML = this.responseText;
			}
		}

		//xhr.send(fd);//发送表单数据
		xhr.send(fd2);//发送直接添加的数据

	}
		
	</script>
</head>
<body>
	<form id="myForm">
		用户名<input type="text" name="username"/><br/>
		密码<input type="text" name="password"/><br/>
		邮箱<input type="text" name="email"/><br/>
		性别<input type="text" name="gender"/><br/>
		<input type="button" name="send" value="发送" οnclick="mySend()"></input>
	</form>
	<div id="debug"></div>
</body>
</html>

后台测试代码formData.php

<?php 
print_r($_POST);
 ?>

3、file上传

有了FormData,那么我们可以先通过dom获取文件对象,然后添加到FormData中,接着用HTTP POST的方式进行发送,在后台的$_FILES便可以接受到文件信息,然后通过move_uploaded_file来移动文件,但是要提前建立好要保存的路径的文件夹

前台测试代码fileApi.html

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>fileApi</title>
	<script type="text/javascript">
		function selectFile() {
			var pic = document.getElementsByTagName("input")[0].files[0];//file为一个文件数组,files[0]为该pic文件
			//console.log(pic);
			var disp = document.getElementById("disp");
			//创建FormData对象
			var fd = new FormData();
			//把文件内容追加到FormData对象中
			fd.append('pic',pic);

			var tmpImg = document.createElement("img");
			tmpImg.src = window.URL.createObjectURL(pic);//把二进制文件对象转为浏览器可以显示的资源src,然后用img加载
			document.getElementsByTagName('body')[0].appendChild(tmpImg);//添加该img元素到浏览器中

			//xhr创建、链接、发送
			var xhr = new XMLHttpRequest();
			xhr.open("post","fileApi.php",true);
			xhr.onreadystatechange = function() {
				if(this.readyState==4 && this.status==200){
					disp.innerHTML = xhr.responseText;
				}
			}
			xhr.send(fd);
			/*var cont = "";
			cont += '文件名称:'+pic.name+'<br>';
			cont +="文件大小"+pic.size+'<br>';
		    disp.innerHTML = cont;*/
		}
	</script>
</head>
<body>
	<input type="file" name="pic" οnchange="selectFile()"></input>
	<div id="disp"></div>
</body>
</html>

后台测试代码fileApi.php

<?php 
if(empty($_FILES)){
	//如果数组为空,则报错
	exit("no file");
}
if($_FILES['pic']['error']){
	//如果error值为1,则报错
	exit("fail");
}
move_uploaded_file($_FILES['pic']['tmp_name'],'upload/'.$_FILES['pic']['name']);
// move_uploaded_file($_FILES['pic']['tmp_name'],'upload/'.$_FILES['pic']['name']);
print_r($_FILES);
?>

4、给上传文件加上进度条

XHR对象有要给属性为upload,绑定了onprogress的函数,通过该函数的事件,算出已加载和全部的比例,并通过css显示

<pre name="code" class="html"><!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>fileApi</title>
	<script type="text/javascript">
		function selectFile() {
			var pic = document.getElementsByTagName("input")[0].files[0];//file为一个文件数组,files[0]为该pic文件
			//console.log(pic);
			var disp = document.getElementById("disp");
			//创建FormData对象
			var fd = new FormData();
			//把文件内容追加到FormData对象中
			fd.append('pic',pic);

			//显示上传的图片
			var tmpImg = document.createElement("img");
			tmpImg.src = window.URL.createObjectURL(pic);//把二进制文件对象转为浏览器可以显示的资源src,然后用img加载
			document.getElementsByTagName('body')[0].appendChild(tmpImg);//添加该img元素到浏览器中

			//xhr创建、链接、发送
			var xhr = new XMLHttpRequest();
			xhr.open("post","fileApi.php",true);
			xhr.onreadystatechange = function() {
				if(this.readyState==4 && this.status==200){
					//disp.innerHTML = xhr.responseText;//得到返回的文件信息
				}
			}

			/*显示进度条
			* HTML5中XHR对象有属性upload绑定了 上传过程 的监听事件
			* ev.total代表总的上传大小,ev.loaded代表意已上传的大小
			* ev.lengthComputable如果为true说明一次性上传,未分开
			* 然后通过百分比来显示
			**/
			xhr.upload.onprogress = function(ev){
				// alert("upload");
				// console.log(percent);
				if(ev.lengthComputable){
					//判断是否为一次性上传
					var percent = 100*ev.loaded/ev.total;
					document.getElementById("bar").style.width = percent+'%';
					document.getElementById("bar").innerHTML = parseInt(percent)+'%';
				}
				
			}
			xhr.send(fd);
			/*var cont = "";
			cont += '文件名称:'+pic.name+'<br>';
			cont +="文件大小"+pic.size+'<br>';
		    disp.innerHTML = cont;*/
		}
	</script>
	<style type="text/css">
	#disp{
		width:500px;
		height:30px;
		border: 1px solid green;
	}
	#bar{
		width:0%;
		height: 100%;
		background: green;
	}
	</style>
</head>
<body>
	<input type="file" name="pic" οnchange="selectFile()"></input>
	<div id="disp">
		<div id="bar"></div>
	</div>
</body>
</html><span style="font-family: Arial, Helvetica, sans-serif;">		</span>
 

实际上,还需要说明一点,

在HTML5中,ajax的跨域有了新的规则,能否跨域取决于对方的应答!

对方服务器如果愿意接受远程过来的Ajax,或某些指定域名的Ajax请求,,可以在header头信息中,添加Access-Control-Allow-Origin:*

5、文件分段上传

如果碰到需要分段上传的文件,那么就需要用到HTML5的新特性。File对象继承自Blob,Blob有函数为slice可用于切割文件,然后通过while循环分段上传即可。

注:在测试过程中,我试着上传了一个mp4格式的视频,出现了问题,首先要想上传成功,得设置成同步,然后每片的大小不能够太大,不然就无法成功,我每片上传2M的大小,这一点存疑。

照着这个思路走下去并没有错,唯一的问题是由于while循环一直在跑,并且Js中的进度条一直在随着每次循环改变,浏览器对页面做出的渲染反应是:等到循环结束才开始对进度条的css进行渲染,这导致了进度条无法实时进行更新。那么加上延迟呢,由于Js并没有诸如sleep之类的延迟函数,并且要多次访问File文件,所以采用了setIntervavl,进行定时访问,为了解决全局变量的问题,使用了Js函数的闭包。

测试代码bigFileApi.html

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>bigFile</title>
	<script type="text/javascript">
		/*
		* 用到的API
		* file->继承自->Blob
		* Blob有slice方法,可以截取二进制对象的一部分
		*
		* 截取10M,然后上传
		* 判断文件有没有截取完毕
		* while(判断){
		*  截取;
		*  ajax上传;
		* }
		 */
		var clock = null;
		function fire(){
			clock = window.setInterval(sendFile, 1000);
		}
		var sendFile = (function(){
			var fd = null;
			var xhr = new XMLHttpRequest();
 			var percent = 0;
 			const LENGTH = 2*1024*1024;//设定片的大小为10M
			var start = 0;//声明和初始化开始
			var end = start+LENGTH;//声明和初始化结尾
			// var oFile = document.getElementsByName("upFile")[0].files[0];//获取文件对象
			var oFileChip = new Blob();//File对象继承自Blob对象,Blob对象中有一方法为slice,可截取文件对象中的某一段
			//var totalSize = oFile.size;//获取总长度,作为结束判断的条件
			// console.log(oFile);
			//以上都是初始化工作,为了达到实时刷新,使用定时器每隔1s来执行sendFile的动作
			//工作都是发生在页面加载之后,该匿名函数执行完成初始化工作,并将xml的操作放于内部函数中返回,赋于变量sendFire
			//
			//当选择文件之后,会由定时器触发调用该函数
			var sending = false;//true表示文件正在上传,false表示文件不在上传状态
			return (function(){
				if(sending==true){
					return;//如果定时器再次访问的时候发现上一次文件尚未上传完毕,则返回等待下一次定时器的访问
				}
				var oFile = document.getElementsByName("upFile")[0].files[0];//获取文件对象
				if(start>oFile.size){
					clearInterval(clock);
					return;	
				}
				oFileChip = oFile.slice(start, end);
				fd = new FormData();
				//console.log(oFileChip);
				fd.append('part',oFileChip);
				xhr.open("post","./bigFileApi.php",false);
				xhr.send(fd);
				start = end;
				end = start+LENGTH;
				sending = false;//表明上传完毕
				percent = parseInt(100*end/oFile.size);
				if(percent>100){
					percent = 100;
				}
				document.getElementById("bar").style.width = percent+"%";
				document.getElementById("bar").innerHTML = percent+"%";
			});
		})();
		// function sendFile() {
			// while(start<totalSize){
			// 	oFileChip = oFile.slice(start, end);//截取小片
			// 	fd = new FormData();
			// 	console.log(oFileChip);
			// 	fd.append('part',oFileChip);//包装成FormData

			// 	xhr = new XMLHttpRequest();
			// 	xhr.open("post","./bigFileApi.php",false);
			// 	//此处使用了同步的方式是为了避免传送文件片的时候造成的混乱
			// 	//但是若是用了同步,则在send的过程中onprogress无法响应,因此不能使用onprogress方式来表现进度条
			// 	xhr.upload.onprogress = function(ev){
			// 		var percent = 100*(start+ev.onloaded)/totalsize;
			// 		document.getElementById("bar").style.width = percent+'%';
			// 		document.getElementById("bar").innerHTML = parseInt(percent)+'%';
			// 	}
			// 	xhr.send(fd);//发送小片包装后的FormData
			// 	//用end显示进度条
			// 	percent = 100*end/totalSize;
			// 	alert(percent);
			// 	document.getElementById("bar").style.width = percent+'%';
			// 	document.getElementById("bar").innerHTML = parseInt(percent)+'%';
			// 	start = end;//设定下一片的开始
			// 	end = start+LENGTH;//设定下一片的末尾
			// }
		// }
	</script>
	<style type="text/css">
		#progress{
			border:1px solid green;
			width: 500px;
			height: 30px;
		}
		#bar{
			background: green;
			width: 0%;
			height: 100%;
		}
	</style>
</head>
<body>
	<h1>html5大文件切割上传</h1>
	<div id='progress'>
		<div id="bar"></div>	
	</div>
	<input type="file" name="upFile" οnchange="fire()"></input>
</body>
</html>


后天代码bigFileApi.php负责把传递过来的文件clips拼接起来,为此,首先要判断文件是否存在,如果不存在,则将临时文件存储至指定目录,如果存在,则对已经存在的文件进行追加。

<?php 
if(!file_exists('./upload/up.mp4')){
	move_uploaded_file($_FILES['part']['tmp_name'],'./upload/up.mp4');
}else{
	file_put_contents('./upload/up.mp4',file_get_contents($_FILES['part']['tmp_name']),FILE_APPEND);
}
echo "ok";

 ?>


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值