最新消息:

XMLHttpRequest实现HTTP协议下文件上传断点续传

js代码 admin 3425浏览 0评论

一、网盘割据的时代

不知大家有没有观察过,在秋季,也就是眼下这个时间,当阵风挂起的时候,地上的落叶就会以一个接一个,翻滚着一同被吹走,这就是“跟风”。老祖宗确实很有智慧,造出来的词语源于生活,又高于生活。

眼下,又是另一波跟风之势——“网盘”,犹如当年团购一样。不过,网盘还是有一定的技术和其他成本,因此,还不像团购那样“落叶漫天飞舞”的状态。但是,各大公司相继介入,可谓又是另外一场群雄之战。

1

 

我并不是专业的产品人,也不是公司的决策者,因此,面对一些纷杂繁芜的现象,自己无心去深入,也不会去评价。立志做个技术人,因此,我所关心的可能就是技术实现,至于孰对孰错,恩怨情仇,公关伎俩,生死天命等一概作云烟从眼前飘过。

网盘中有个很重要的,可以说是核心的功能,就是文件上传,So,本文就来说说这个上传,如何在HTTP协议下实现文件的断点续传呢?

二、文件断点续传的实现

目前从实用技术角度讲,文件上传的断点续传实现主要是借助客户端,例如,我们首次进入某网盘,会看到下图所示的“控件安装”提示:

222

 

有的网盘也有文件上传暂停的功能,这似乎是借助swfUpload实现的。

也就是,这些带续传功能的上传都不是使用HTTP协议实现的,也就是不是传统的网页技术(HTML+CSS+JS)实现的。

然,times are changing, 事物发展,时代变化。以前的一些所谓的“不能”、“不可能”都将成为过去。

上月一篇独苗文章介绍了XMLHttpRequest level 2(下简称Ajax 2.0)中的一些支持的数据格式,如果稍微关注,应该知道,Ajax 2.0中最大的变化之一就是对二进制数据的支持,而且提供了一个可以直接处理二进制数据的方法——slice方法。

JS中的字符串有slice方法,数组也有。Ajax 2.0经过一些变化后,现在也和数组、字符串的slice方法语法完全一致了。于是,我们就可以把二进制数据流想象成一些连续的字符串数据,并对这些二进制数据进行slice处理。

比方说怎样的场合呢?

PHP默认似乎有个最大文件上传的限制 – post_max_size,我的本地看了下,是64M.

333

 

此时,我们想一次性传一个80M的动作片精华片段,就会以失败告终。

但是,有了slice方法,我们可以把文件分割,比方说,每20M作为一个请求发送出去,后台再把这些二进制数据拼合成一个完整文件。

slice(0, 20); slice(20, 40); slice(40, 60); slice(60)

还有一个很重要的场合就是断点续传

文件传输是个具有时间周期的过程,从玩三国杀的离线率可以看出,掉线什么的是常有的事情。显然,传文件必定会存在传着传着就死在99%位置的情况。

你想啊,大鼻孔姐的片子想放到网盘里,随时随地可以欣赏。结果看着进度条等了40分钟,好不容易传到99%,突然断电…………开机后,发现又要重传,是不是小弟弟要气得短小软?显然,后果很严重哈~

因此,对于大文件而言,断电续传功能很重要。有了Blob数据格式的slice方法,一切都变得简单了。

我的思路是这样的,有两条:
1. 浏览器记住(如localStorage)最近一次成功传输的位置;当再次上传这个图片的时候,直接从浏览器存储的位置开始传。
2. 浏览器不做任何事情,在上传之前先去后台走一遍,看看目前此文件是否存在,以及存在的大小,返回给浏览器,然后浏览器再决定上传的起始位置。

理性的分析以及实践的结果表明,第二种思路可行性更高。//zxx: 并不一定是最好的思路,您可以自己想出更精彩的实践方法

如果用文字举例的话就是:
某老师的视频是80*1024*1024B, 我们每次传1024*1024B,也就是1M,假设传了79M了,结果大脚一抖,电源关掉有木有!某老师就这样随风逝去了……

用户重新开机,决定再次传这个80M的视频。当用户选择了这个文件后,我们先去后台走一圈,把当前已经传好的文件大小反馈给客户端(Ajax 1.0就可以),JS拿反馈大小和源文件大小一比对,奶奶的,残缺啊!于是,就从残缺位置slice,这里就是:

file.slice(79*1024*1024)

接着之前的只传了1M就OK啦!于是,断点续传实现。

由于网页本身的局限性,我们没法直接触发本地文件的上传。因此,目前而言,断点文件还是要用户选择(相比客户端上传软件多了这一步)。

三、文件断点续传的实践

2

 

您可以狠狠地点击这里:HTTP协议下Ajax实现的文件断点续传demo

说明:

  1. 上demo可以说是一个比较完善的上传体验,有删除,重传,续传,进度条等常用功能。
  2. 基于Ajax 2.0的二进制文件传输实现,因此,IE10+,Chrome以及Firefox等浏览器支持。另外JS原生,无外部依赖。
  3. 空间的流量月月吃紧,经常溢出不够用,因此,为了节约成本,只允许最大200K的文件。所以,大家想要测试断点续传效果,可以通过工具,把自己的浏览器速度限制到10~20K每秒。
  4. demo页面每100K就会对文件进行一次分割上传,因此,100~200K大小文件会有看到2次上传请求。PS:实际开发时候,应该至少1M一次分割。
  5. 为了直观表现断点上传的功能,我会截一个本地的视频给大家演示,并原声重现。
  6. 后台PHP很简单,就是追加二进制数据,file_put_contents方法有个FILE_APPEND选项,直接追加,很好用的!
  7. 正好研究了下开源协议,这里请容我练个:demo页面中的JS+CSS+HTML源代码遵循GPL协议,即您可以任意复制,修改,但是,只能作为学习或私人项目使用,不能作为商业软件的一部分去出售。如有疑问,可zhangxinxu@zhangxinxu.com联系。

其他一些技术细节,懂的人不说也知道是怎么回事,不懂的人要花很大功夫斟酌表达才可以。然,目前时机不成熟,付出收益比略低,因此,这里不具体叙述,有问题欢迎邮箱联系。下面视频演示下断点续传的效果:

视频展示:
因为半夜三更录的视频,老婆大人已经安然就寝,所以自己声音比较小,发音也不太准,貌似被优酷搞糊了,大家凑合看看吧~~

视频地址:http://v.youku.com/v_show/id_XNjMwNjE3NzY4.html

四、说点题外话

上月本人只更新了一篇文章。有一周是整周的培训。然后除了研究现在这个Ajax断点续传,还在做一个自己的开源项目(已经用了两周了,本周应该可以结束)。

本人最近有在纠结专利与开源的事情。我司申请专利很是方便,而且公司鼓励,且对个人发展有很有帮助,我显然是要积极做这些事情的。但是,个人价值观 中很重要的一点就是要留下什么,因此,自己是非常喜欢共享东西的。于是就存在一个矛盾,有些东西,例如一些创新的想法或者实现方式,如果成为的专利,就不 好拿出来随意分享了,因为,自己申请的专利不是自己的而是公司的,自己完全没有把控权。头疼~~

不过,今天例会跟组里前辈沟通了下,算是想通了。这样子,自己平时业余时间研究的东西,即使很赞,很具有专利性,我也会及时和大家一起探讨与进步;如果是实际工作发现的创新性想法,则走专利路线,技术封闭。我举得这样的权衡应该是很不错的,不知大家的看法如何呢?

哈哈,今天特地研究了下开源版权的问题,学到了不少,各种License都大致了解了一点,文中好像就有体现了,

博客所有技术文章遵循CC协议,即署名、非商业性使用、相同方式共享、禁止演绎。

一些开源插件等遵循其他协议,回头补上~~

文章来自:张鑫旭-鑫空间-鑫生活

作者上传界面源码:

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="description" content="Ajax文件上传断点续传 » 张鑫旭-鑫空间-鑫生活" />
<meta name="description" content="张鑫旭web前端学习实例页面 Ajax文件上传断点续传" />
<meta name="keywords" content="web前端, css, jQuery, javascript" />
<meta name="author" content="张鑫旭, zhangxixnu" />
<title>Ajax文件上传断点续传 » 张鑫旭-鑫空间-鑫生活</title>
<link rel="stylesheet" href="../css/demo.css" type="text/css" />
<link rel="stylesheet" href="btn.css" type="text/css" />
<link rel="stylesheet" href="../css/hl.css" type="text/css" />
<style>
ul { padding: 0; margin: 0; list-style-type: none; }
.upload { width: 600px; margin: 0 auto; }
.form { padding: 12px 0 30px; }
.file { position: absolute; width: 230px; height: 34px; cursor: pointer; opacity: 0; }
.file:hover + .btn-info { background-color: #39b3d7; border-color: #269abc; }
[type="submit"] { width: 100px; margin-left: 260px; float: right; visibility: hidden; }
.upload_ul { display: none; width: 100%; border: 1px solid #bbb; background-color: #fff; font-size: 12px; }
.upload_ul > li { display: table-row; opacity: 1; overflow: hidden; -webkit-transition: opacity .2s; transition: opacity .2s;
	background-image:-webkit-linear-gradient(top, #ABD9FF, #88C9FF);
	background-image:        linear-gradient(to bottom, #ABD9FF, #88C9FF);
	-moz-background-size: 0% 100%;
	background-size: 0% 100%;
	background-repeat: no-repeat;
}
.upload_title { color: #666; background-color: #f0f0f0; }
.upload_cell { display: table-cell; padding: 5px 10px; border-top: 1px solid #ddd; vertical-align:middle; }
.upload_cell:first-child { width: 50%; }
.waiting{ color: #999; }
.uploading { color: #069; }
.canceling { color: #CE4625; }
.success { color: green; }
.error { color: #f30; }
.remind { padding: 5px 10px; background-color: #eee; margin-top: 40px; color: #666; font-size: 12px;}

/*icon copyright by weiyun */
.icon { display: inline-block; width: 30px; height: 30px; background: url(file-small.png) no-repeat -74px -490px; vertical-align: middle; }
.icon-doc,.icon-docx{background-position:0 0}
.icon-ppt,.icon-pptx{background-position:-37px 0}
.icon-xls,.icon-xlsx{background-position:-74px 0}
.icon-pdf,.ico-pdf{background-position:-111px 0}
.icon-txt,.ico-txt{background-position:-148px 0}
.icon-msg,.ico-msg{background-position:0 -35px}
.icon-rp,.ico-rp{background-position:-37px -35px}
.icon-vsd,.ico-vsd{background-position:-74px -35px}
.icon-ai,.ico-ai{background-position:-111px -35px}
.icon-eps,.ico-eps{background-position:-148px -35px}
.icon-log,.ico-log{background-position:0 -70px}
.icon-xmin,.ico-xmin{background-position:-37px -70px}
.icon-cab,.ico-cab{background-position:-74px -70px}
.icon-psd,.ico-psd{background-position:0 -105px}
.icon-jpg,.ico-jpg{background-position:-37px -105px}
.icon-jpeg,.ico-jpeg{background-position:-37px -105px}
.icon-png,.ico-png{background-position:-74px -105px}
.icon-gif,.ico-gif{background-position:-111px -105px}
.icon-bmp,.ico-bmp{background-position:-148px -105px}
.icon-rmvb,.ico-rmvb{background-position:0 -140px}
.icon-rm,.ico-rm{background-position:0 -140px}
.icon-mod,.ico-mod{background-position:-37px -140px}
.icon-mov,.ico-mov{background-position:-74px -140px}
.icon-3gp,.ico-3gp{background-position:-111px -140px}
.icon-avi,.ico-avi{background-position:-148px -140px}
.icon-swf,.ico-swf{background-position:0 -175px}
.icon-flv,.ico-flv{background-position:-37px -175px}
.icon-mpe,.ico-mpe{background-position:-74px -175px}
.icon-asf,.ico-asf{background-position:-111px -175px}
.icon-wmv,.ico-wmv{background-position:-148px -175px}
.icon-mp4,.ico-mp4{background-position:-185px -175px}
.icon-wma,.ico-wma{background-position:0 -210px}
.icon-mp3,.ico-mp3{background-position:-37px -210px}
.icon-wav,.ico-wav{background-position:-74px -210px}
.icon-apk,.ico-apk{background-position:0 -245px}
.icon-ipa,.ico-ipa{background-position:-37px -245px}
.icon-exe,.ico-exe{background-position:-74px -245px}
.icon-msi,.ico-msi{background-position:-111px -245px}
.icon-bat,.ico-bat{background-position:-148px -245px}
.icon-fla,.ico-fla{background-position:0 -280px}
.icon-htm,.ico-htm,.icon-html,.ico-html{background-position:-37px -280px}
.icon-c,.ico-c{background-position:-111px -280px}
.icon-xml,.ico-xml{background-position:-148px -280px}
.icon-asp,.ico-asp{background-position:-185px -280px}
.icon-chm,.ico-chm{background-position:0 -315px}
.icon-hlp,.ico-hlp{background-position:-37px -315px}
.icon-ttf,.ico-ttf{background-position:-111px -315px}
.icon-otf,.ico-otf{background-position:-148px -315px}
.icon-rar,.ico-rar{background-position:0 -350px}
.icon-zip,.ico-zip{background-position:-37px -350px}
.icon-tar,.ico-tar{background-position:-74px -350px}
.icon-cab,.ico-cab{background-position:-111px -350px}
.icon-uue,.ico-uue{background-position:-148px -350px}
.icon-jar,.ico-jar{background-position:0 -385px}
.icon-7z,.ico-7z{background-position:-37px -385px}
.icon-iso,.ico-dmg{background-position:-74px -385px}
.icon-dmg,.ico-dmg{background-position:-111px -385px}
.icon-ace,.ico-ace{background-position:-148px -385px}
.icon-bak,.ico-bak{background-position:0 -420px}
.icon-tmp,.ico-tmp{background-position:-37px -420px}
.icon-old,.ico-old{background-position:-74px -420px}
.icon-document,.ico-document{background-position:0 -455px}
.icon-exec,.ico-exec{background-position:-37px -455px}
.icon-code,.ico-code{background-position:-74px -455px}
.icon-image,.ico-image{background-position:-111px -455px}
.icon-video,.ico-video{background-position:-148px -455px}
.icon-audio,.ico-audio{background-position:0 -490px}
.icon-compress,.ico-compress{background-position:-37px -490px}
.icon-unknow,.ico-unknow{background-position:-74px -490px}
.icon-filebroken,.ico-filebroken{background-position:-111px -490px}
.icon-link,.ico-link{background-position:-111px -418px}

</style>
</head>

<body>
<div id="header">
	<a href="http://www.zhangxinxu.com/" class="logo" title="回到鑫空间-鑫生活首页">
    	<img src="http://www.zhangxinxu.com/php/image/zxx_home_logo.png" border="0" />
    </a>
</div>
<div id="main">
	<h1>Ajax文件上传断点续传实例页面</h1>
    <div id="body" class="light">
    	<div id="content" class="show">
        	<h3>Ajax断点续传展示</h3>
            <div class="article_new"><a href="http://www.zhangxinxu.com/wordpress/?p=3754">回到相关文章 »</a></div>
            <div class="demo">
            	<div class="upload">
                    <form id="form" class="form" action="upload.php">
                        <input type="file" id="file" class="file" name="file[]" multiple><span class="btn btn-info">请选择要上传的文件(小于200K)</span>
                        <input type="submit" id="submit" class="btn btn-primary" value="上传">
                    </form>
                    <ul id="uploadUl" class="upload_ul">
                        <li class="upload_title">
                            <div class="upload_cell">标题</div>
                            <div class="upload_cell">类型</div>
                            <div class="upload_cell">大小</div>
                            <div class="upload_cell">状态</div>
                            <div class="upload_cell">操作</div>
                        </li>
                    </ul>
                    <p class="remind">1. 点击默认展示的按钮选择文件;<br>2. 点击后出现的上传按钮进行上传</p>
                </div>
            </div>
        </div>       
    </div>
</div>
<script id="fileTemplate" type="text/template">
<li id="filelist_$id$">
	<div class="upload_cell">$name$</div>
	<div class="upload_cell"><i class="icon icon-$type$"></i></div>
	<div class="upload_cell">$size$</div>
	<div id="filestatus_$id$" class="upload_cell">$status$</div>
	<div id="fileoperate_$id$" class="upload_cell">$operate$</div>
</li>
</script>
<script>
var $ = function(id) {
	return document.getElementById(id);
};

// 一些元素
var eleForm = $("form"), eleFile = $("file"), eleSubmit = $("submit"),
	// 文件等待上传列表的容器
	eleUploadUl = $("uploadUl"),
	// 模板元素
	eleTemplate = $("fileTemplate");

// 上传文件队列数组
var fileArray = [], 
// 文件分割上传的间隙大小
fileSplitSize = 1024 * 100,
// 模板HTML
htmlTemplate = eleTemplate && eleTemplate.innerHTML || '';
if (typeof history.pushState == "function") {	
	// 一些元素的状态
	var objStateElement = (function() {		
		var _$ = function(name, fileid) {
			return $("file"+ name +"_" + fileid) || { innerHTML: "" };
		};						

		return {
			// 上传进度条背景的控制
			backgroundSize: function(params, percent) {
				var dom = typeof params == "string"? $("filelist_" + params): params;
				if (dom) {
					dom.style.mozBackgroudSize = percent + "% 100%";
					dom.style.backgroundSize = percent + "% 100%";
				}
			},			
			wait: function(fileid) {
				// 一些状态的改变
				_$("status", fileid).innerHTML = '<span class="uploading">上传中...</span>';
				_$("operate", fileid).innerHTML = '<a href="javascript:" data-type="pause" data-id="'+ fileid +'">暂停</a>';	
			},
			keep: function(fileid) {
				_$("status", fileid).innerHTML = '<span class="waiting">等待续传...</span>';
			},
			success: function(fileid, time) {
				var eleList = $("filelist_" + fileid), 
					eleOperate = $("fileoperate_" + fileid), 
					eleStatus = $("filestatus_" + fileid);

				// 进度条隐藏
				this.backgroundSize(eleList, "0");

				// 因为元素不会删除,因此,有必要清除id
				eleStatus.id = "";
				eleOperate.id = "";
				eleList.id = "";
				// 删除本地存储的起点,完全上传成功,因此没有续传的必要
				localStorage.removeItem(fileid);
				// 状态信息再变化
				eleStatus.innerHTML = '<span class="success">'+ ((performance.now() - time > 1000)? "上": "秒") +'传成功!</span>';
				eleOperate.innerHTML = '';

				console.log([performance.now(), time].join());
			},
			error: function(fileid) {
				// 状态信息再变化
				_$("status", fileid).innerHTML = '<span class="error">出现异常!</span>';
				_$("operate", fileid).innerHTML = '<a href="javascript:" data-type="try" data-id="'+ fileid +'">重试</a>';	
			}	
		}							
	})();

	// 单文件上传
	var funFileUpload = function(fileid, onsuccess, onerror, onpause) {
		var file = fileArray[fileid], now = performance.now();
		if (!fileid || !file) return;
		onsuccess = onsuccess || function() {
			funFileUpload(fileArray[0]);	
		};
		onerror = onerror || function() {
			funFileUpload(fileArray[fileArray.indexOf(fileid) + 1]);	
		};
		onpause = onpause || function() {
			funFileUpload(fileArray[fileArray.indexOf(fileid) + 1]);	
		};
		if (file.flagPause == true) {
			onpause.call(fileid);
			return;
		}

		objStateElement.wait(fileid);

		// 文件分割上传
		// 文件大小和分割起点
		// 注释的是本地存储实现
		var size = file.size, /*start = localStorage[fileid] * 1 || 0*/ start = $("filelist_" + fileid).filesize;
		if (size == start) {
			// 已经传过了
			fileArray.shift();
			if (delete fileArray[fileid]) console.log(fileArray.join() + "---上传成功");
			objStateElement.success(fileid, now);
			// 回调
			onsuccess.call(fileid, {});
			return;
		}

		var funFileSize = function() {
			if (file.flagPause == true) {
				onpause.call(fileid);
				return;
			}
			var data = new FormData();
			data.append("name", encodeURIComponent(file.name));
			data.append("fileid", fileid);
			data.append("file", file.slice(start, start + fileSplitSize));
			data.append("start", start + "");

			// XMLHttpRequest 2.0 请求
			var xhr = new XMLHttpRequest();
			xhr.open("post", eleForm.action, true);				
			xhr.setRequestHeader("X_Requested_With", location.href.split("/")[5].replace(/[^a-z]+/g,"$"));

			// 上传进度中
			xhr.upload.addEventListener("progress", function(e) {
				objStateElement.backgroundSize(fileid, (e.loaded + start) / size * 100);
			}, false);
			// ajax成功后
			xhr.onreadystatechange = function(e) {
				if (xhr.readyState == 4) {
					if (xhr.status == 200) {
						try {
							var json = JSON.parse(xhr.responseText);
						} catch (e) {
							objStateElement.error(fileid);
							return;
						} 
						//var json = JSON.parse(xhr.responseText);
						if (!json || !json.succ) {
							objStateElement.error(fileid);
							onerror.call(fileid, json);
							return;
						}

						if (start + fileSplitSize >= size) {
							// 超出,说明全部分割上传完毕
							// 上传队列中清除者一项
							fileArray.shift();
							if (delete fileArray[fileid]) console.log(fileArray.join() + "---上传成功");
							objStateElement.success(fileid, now);
							// 回调
							onsuccess.call(fileid, json);
						} else {
							// 尚未完全上传完毕						
							// 存储上传成功的文件点,以便出现意外的时候,下次可以断点续传
							localStorage.setItem(fileid, start + "");
							// 改变下一部分文件的起点位置
							start += fileSplitSize;
							console.log(start);
							// 上传下一个分割文件
							funFileSize();
						}		
					} else {
						objStateElement.error(fileid);
					}
				}
			};

			xhr.send(data);
		};

		// 文件分割上传开始
		funFileSize();
	};

	// IE10+, Chrome, FireFox等~
	eleForm.addEventListener("submit", function(event) {
		funFileUpload(fileArray[0]);		
		event.preventDefault();
	});

	// 选择文件后
	eleFile.addEventListener("change", function(event) {
		// 获取文件
		var files = event.target.files;
		// 遍历文件,显示文件列表信息
		var htmlFile = '', index = 0, length = files.length;
		for (; index < length; index++) {
			var file = files[index];    // Blob对象相关知识可参考:http://www.zhangxinxu.com/wordpress/?p=3725
			// console.log(file.type);
			var name = file.name, size = file.size, type = file.type || "", id = (file.lastModifiedDate + "").replace(/\W/g, '') + size + type.replace(/\W/g, '');
			var objHtml = {
				id: id,
				type: 'cloud',
				name: name,
				size: size + "B",
				status: '<span class="waiting">等待上传</span>',
				operate: '<a href="javascript:" data-type="delete" data-id="'+ id +'">删除</a>'
			}
			// name 50字符限制
			if (name.length > 50) {
				objHtml.name = '<span title="'+ name +'">'+ name.slice(0,20) + '...' + name.slice(-20) +'</span>';
			}

			// 文件类型与对应的图标
			var format = name.split(".");
			objHtml.type = format[format.length - 1] || "unknown";

			// 如果大小大于1M使用'M'为单位表示, 1位小数点
			if (size > 1024 * 1024) {
				objHtml.size = Math.round(size / (1024 * 1024) * 10) / 10 + "M";
			} else if (size > 1024) {
				// 如果大小大于1KB使用'KB'为单位表示, 1位小数点
				objHtml.size = Math.round(size / 1024 * 10) / 10 + "KB";
			}

			if (size > 1024 * 200) {
				// 如果文件大于200K, 状态为'大小溢出'
				objHtml.id = Math.random();
				objHtml.status = '<span class="error">文件过大</span>';
				objHtml.operate = '';
			} else if (fileArray.indexOf(id) != -1) {
				// 如果文件已经在列表中
				objHtml.id = Math.random();
				objHtml.status = '<span class="error">文件已存在</span>';
				objHtml.operate = '';
			} else {
				// 加入文件队列,并缓存对应的文件二进制对象
				fileArray.push(id);
				fileArray[id] = file;
			}

			htmlFile += htmlTemplate.replace(/\$\w+\$/gi, function(matchs) {
				var returns = objHtml[matchs.replace(/\$/g, "")];		
				return (returns + "") == "undefined"? "": returns;
			});			
		}
		// 载入HTML
		if (htmlFile !== '') {
			eleSubmit.style.visibility = "visible";
			eleUploadUl.style.display = "table";
			eleUploadUl.insertAdjacentHTML("beforeEnd", htmlFile);

			// 初始化进度条

			// 下面注释的是本地存储方法
			/*fileArray.forEach(function(fileid) {
				var loaded = localStorage[fileid] * 1;
				if (loaded > 0) {
					objStateElement.backgroundSize(fileid, loaded / fileArray[fileid].size * 100);
				}
			});*/

			// 现在直接使用Ajax请求
			var nameArray = fileArray.map(function(fileid) {
				var nameSplit = fileArray[fileid].name.split("."),
				name = nameSplit[nameSplit.length - 1];

				return fileid + "." + name;
			});
			var xhr_filesize = new XMLHttpRequest();
			xhr_filesize.open("GET", "filesize.php?filename=" + nameArray.join(), true);
			xhr_filesize.onreadystatechange = function(e) {
				if (xhr_filesize.readyState == 4) {
					if (xhr_filesize.status == 200 && xhr_filesize.responseText) {
						var json = JSON.parse(xhr_filesize.responseText);
						if (json.succ && json.data) {
							for (var key in json.data) {
								if (json.data[key] > 0 && json.data[key] < fileArray[key].size) {									
									objStateElement.backgroundSize(key, json.data[key] / fileArray[key].size * 100);
									objStateElement.keep(key);
								} 
								$("filelist_" + key).filesize = json.data[key];
							}
						}
					}
				}
			};
			xhr_filesize.send();
		}

		eleForm.reset();
	});
	// 文件删除等委托事件
	eleUploadUl.addEventListener("click", function(event) {
		var target = event.target, id = target && target.getAttribute("data-id");
		if (id && /^a$/i.test(target.tagName)) {
			switch (target.getAttribute("data-type")) {
				case "delete": {
					var filelist = $("filelist_" + id);
					if (filelist) {
						filelist.style.opacity = 0;

						// 源数据清除
						fileArray.splice(fileArray.indexOf(id), 1);
						if (delete fileArray[id]) console.log(fileArray.join() + "---删除成功");

						setTimeout(function() { 
							filelist.parentNode.removeChild(filelist); 
							if (fileArray.length == 0) {
								eleSubmit.style.visibility = "hidden";
								eleUploadUl.style.display = "none";
							}
						}, 220);						
					}
					break;
				}
				case "pause": {
					var eleStatus = $("filestatus_" + id);
					if (fileArray[id]) {
						fileArray[id].flagPause = true;	
						target.setAttribute("data-type", "reupload");
						target.innerHTML = "继续";
						if (eleStatus) eleStatus.innerHTML = "上传暂停";
					}
					break;
				}
				case "try": case "reupload": {
					funFileUpload(id, function() {}, function() {}, function() {});
				}
			}
		}
	});

} else {
	eleUploadUl.style.display = "block";
	eleUploadUl.innerHTML = '<li class="error"><p style="margin:.5em 1em;">当前浏览器不支持!试试IE10+, Chrome等~</p></li>';
}
</script>
<div id="footer">
    Designed &amp; Powerd by <a href="http://www.zhangxinxu.com/">zhangxinxu</a><br />
    Copyright© 张鑫旭-鑫空间-鑫生活<br>
    <a href="http://www.miibeian.gov.cn/" target="_blank">鄂ICP备09015569号</a>      
</div>

<div id="ad" style="right: 0; top: 0; text-align: center;">
	<a title="小小赞助大大帮助" href="https://me.alipay.com/zhangxinxu" target="_blank"><img width="159" height="37" align="absmiddle" alt="支付鼓励" src="/wordpress/wp-content/themes/default/images/pay_encourage.png"></a>
</div>
</body>
</html>

转载请注明:jinglingshu的博客 » XMLHttpRequest实现HTTP协议下文件上传断点续传

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址