JavaScript中三种异步上传文件方式


Posted in Javascript onMarch 06, 2016

异步上传文件是为了更好的用户体验,是每个前端必须掌握的技能。这里我提出三点有关异步文件上传的方式。

使用第三方控件,如Flash,ActiveX等浏览器插件上传。

使用隐藏的iframe模拟异步上传。

使用XMLHttpRequest2来实现异步上传。

第一种使用浏览器插件上传,需要一定的底层编码功底,在这里我就不讲了,以免误人子弟,提出这点大家可以自行百度。

第二种使用隐藏的iframe模拟异步上传。为什么在这里说的是模拟呢?因为我们其实是将返回结果放在了一个隐藏的iframe中,所以才没有使当前页面跳转,感觉就像是异步操作一样。

先贴出代码:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-">
<title>隐藏的iframe上传文件</title>
<script type="text/javascript" src="jquery路径..."></script>
</head>
<body>
<iframe name="frm" style="display:none"></iframe>
<form action="/upload" enctype="multipart/form-data" method="post" target="frm" onsubmit="loading(true);">
<p id="upfile">附件: <input type="file" name="myfile" style="display: inline"></p>
<p id="upbtn"><input style="padding-left:px;padding-right: px;" type="submit" value="异步上传">
<span id="uptxt" style="display: none">正在上传...</span></p>
</form>
<div id="flist" style="border:px dotted darkgray;"></div>
<script>
// 上传完成后的回调
function uploadFinished(fileName) {
addToFlist(fileName);
loading(false);
}

function addToFlist(fname) {
var temp = ["<p id='" + fname + "'>",
fname,
"<button onclick='delFile(\"" + fname + "\");'>删除</button>",
"</p>"
];
$("#flist").append(temp.join(""));
}

function loading(showloading) {
if (showloading) {
$("#uptxt").show();
} else {
$("#uptxt").hide();
}
}
</script>
</body>
</html>

这种技术有两个关键的地方:

1.form会指定target,提交的结果定向返回到隐藏的ifram中。(即form的target与iframe的name属性一致)。

2.提交完成后,iframe中页面与主页面通信,通知上传结果及服务端文件信息

如何与主页面通信呢?

我们用nodejs在接收完了文件后返回了一个window.parent.主页面定义的方法,执行后可以得知文件上传完成。代码很简单:

router.post('/upload', multipartMiddleware, function(req, res) {
var fpath = req.files.myfile.path;
var fname = fpath.substr(fpath.lastIndexOf('\\') + );
setTimeout(function() {
var ret = ["<script>",
"window.parent.uploadFinished('" + fname + "');",
"</script>"];
res.send(ret.join(""));
}, );
});

执行后可以打开开发人员选项,你会发现隐藏iframe中返回了服务器的一些数据。

第三种使用XMLHttpRequest2来进行真正的异步上传。

还是先贴出代码:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-">
<title>xhr level 异步上传</title>
<script type="text/javascript" src="jquery路径..."></script>
</head>
<body>
<div>
<p id="upfile">附件: <input type="file" id="myfile" style="display: inline"></p>
<p id="upbtn">
<input style="padding-left:px;padding-right: px;" type="button" value="异步上传" onclick="upload();">
<span id="uptxt" style="display: none">正在上传...</span>
<span id="upprog"></span>
<button id="stopbtn" style="display:none;">停止上传</button>
</p>
</div>
<div id="flist" style="border:px dotted darkgray;"></div>
<script>
function upload() {
// .准备FormData
var fd = new FormData();
fd.append("myfile", $("#myfile")[].files[]);

// 创建xhr对象
var xhr = new XMLHttpRequest();
// 监听状态,实时响应
// xhr 和 xhr.upload 都有progress事件,xhr.progress是下载进度,xhr.upload.progress是上传进度
xhr.upload.onprogress = function(event) {
if (event.lengthComputable) {
var percent = Math.round(event.loaded * / event.total);
console.log('%d%', percent);
$("#upprog").text(percent);
}
};
// 传输开始事件
xhr.onloadstart = function(event) {
console.log('load start');
$("#upprog").text('开始上传');

$("#stopbtn").one('click', function() {
xhr.abort();
$(this).hide();
});

loading(true);
};
// ajax过程成功完成事件
xhr.onload = function(event) {
console.log('load success');
$("#upprog").text('上传成功');

console.log(xhr.responseText);
var ret = JSON.parse(xhr.responseText);
addToFlist(ret.fname);
};
// ajax过程发生错误事件
xhr.onerror = function(event) {
console.log('error');
$("#upprog").text('发生错误');
};
// ajax被取消
xhr.onabort = function(event) {
console.log('abort');
$("#upprog").text('操作被取消');
};
// loadend传输结束,不管成功失败都会被触发
xhr.onloadend = function (event) {
console.log('load end');
loading(false);
};
// 发起ajax请求传送数据
xhr.open('POST', '/upload', true);
xhr.send(fd);
}
function addToFlist(fname) {
var temp = ["<p id='" + fname + "'>",
fname,
"<button onclick='delFile(\"" + fname + "\");'>删除</button>",
"</p>"
];
$("#flist").append(temp.join(""));
}
function delFile(fname) {
console.log('to delete file: ' + fname);
// TODO: 请实现
}
function loading(showloading) {
if (showloading) {
$("#uptxt").show();
$("#stopbtn").show();
} else {
$("#uptxt").hide();
$("#stopbtn").hide();
}
}
</script>
</body>
</html>

代码有点多,但是通俗易懂。使用过AJAX的人都知道,XHR对象提供了一个onreadystatechange的回调方法来监听整个请求/响应过程。在XMLHttpRequest2级规范中又多了几个进度事件。有以下6个事件:

1.loadstart: 在接收到响应数据的第一个字节时触发。

2.progress: 在接收响应期间持续不断地触发。

3.error: 在请求发生错误时触发。

4.abort: 在因为调用abort()方法而终止连接时触发。

5.load: 在接收到完整的响应数据时触发。

6.loadend: 在通信完成或者触发error,abort,load事件后触发。

这次我们可以解读代码:当传输事件开始后,我们便在停止传送按钮上添加点击事件,内置了abort方法可以停止传送。若不点则会正常上传直到传送完毕为止。其后台代码类似第二种方法。

三种方法各有优劣,做个简单的小结吧。

第三方控件交互性和可控性好,因为接近底层,其性能也是很优秀的。但是由于编写难度大通常需要自己安装插件,有时可能需要自己进行编写。

隐藏的iframe方法我个人觉得是非常有思想的一个方法,iframe可以帮我们做很多事。这种方式具有广泛的浏览器兼容性而且不需要安装插件。但是它交互性差,上传过程不可控,而且性能也是很一般的。

XHR2级的纯ajax上传,它必须要版本比较高一点的浏览器(ie9+)。但是它交互性特别好,可以看到上传进度并且是可控的。

开发可按需求来采用不同方法。个人觉得这些文件上传,特别是第二种提供的是一种思想,充分的利用了某些html标签的特性,可能是我们开发人员需要多思考的地方。

Javascript 相关文章推荐
获取DOM对象的几种扩展及简写
Oct 09 Javascript
javascript setTimeout和setInterval 的区别
Dec 08 Javascript
js 兼容多浏览器的回车和鼠标焦点事件代码(IE6/7/8,firefox,chrome)
Apr 14 Javascript
Wordpress ThickBox 点击图片显示下一张图的修改方法
Dec 11 Javascript
js关闭父窗口时关闭子窗口
Apr 01 Javascript
JS 实现图片直接下载示例代码
Jul 22 Javascript
js toFixed()方法的重写实现精度的统一
Mar 06 Javascript
jquery实现图片上传之前预览的方法
Jul 11 Javascript
基于Bootstrap里面的Button dropdown打造自定义select
May 30 Javascript
使用bootstrap typeahead插件实现输入框自动补全之问题及解决办法
Jul 07 Javascript
JavaScript设计模式之策略模式详解
Jun 09 Javascript
微信小程序 wxParse插件显示视频问题
Sep 27 Javascript
JavaScript中获取纯正的undefined的方法
Mar 06 #Javascript
JS面向对象编程详解
Mar 06 #Javascript
深入学习JavaScript的AngularJS框架中指令的使用方法
Mar 05 #Javascript
使用Jasmine和Karma对AngularJS页面程序进行测试
Mar 05 #Javascript
JavaScript的React框架中的JSX语法学习入门教程
Mar 05 #Javascript
在AngularJS框架中处理数据建模的方式解析
Mar 05 #Javascript
简单讲解AngularJS的Routing路由的定义与使用
Mar 05 #Javascript
You might like
利用PHP+JS实现搜索自动提示(实例)
2013/06/09 PHP
php解析mht文件转换成html的实例
2017/03/13 PHP
js实现的网页颜色代码表全集
2007/07/17 Javascript
Mootools 1.2教程 Fx.Tween的使用
2009/09/15 Javascript
javascript学习(一)构建自己的JS库
2013/01/02 Javascript
jquery分页插件jpaginate在IE中不兼容问题
2014/04/22 Javascript
nodejs之请求路由概述
2014/07/05 NodeJs
Jquery设置attr的disabled属性控制某行显示或者隐藏
2014/09/25 Javascript
cookie的secure属性详解
2015/04/08 Javascript
JS制作手机端自适应缩放显示
2015/06/11 Javascript
jQuery网页右侧广告跟随滚动代码分享
2020/04/20 Javascript
AngularJs基本特性解析(一)
2016/07/21 Javascript
图片上传之FileAPI与NodeJs
2017/01/24 NodeJs
JS原生带小白点轮播图实例讲解
2017/07/22 Javascript
基于vue打包后字体和图片资源失效问题的解决方法
2018/03/06 Javascript
Webpack path与publicPath的区别详解
2018/05/03 Javascript
基于JQuery和DWR实现异步数据传递
2020/10/16 jQuery
JS实现简易图片自动轮播
2020/10/16 Javascript
微信小程序开发数据缓存基础知识辨析及运用实例详解
2020/11/06 Javascript
[01:03:59]2018DOTA2亚洲邀请赛3月30日 小组赛B组VGJ.T VS Secret
2018/03/31 DOTA
python strip()函数 介绍
2013/05/24 Python
pyqt4教程之messagebox使用示例分享
2014/03/07 Python
Python文档生成工具pydoc使用介绍
2015/06/02 Python
python 读写、创建 文件的方法(必看)
2016/09/12 Python
python中文分词教程之前向最大正向匹配算法详解
2017/11/02 Python
Python自动化运维_文件内容差异对比分析
2017/12/13 Python
Python多线程中阻塞(join)与锁(Lock)使用误区解析
2018/04/27 Python
对Django 转发和重定向的实例详解
2019/08/06 Python
pytorch三层全连接层实现手写字母识别方式
2020/01/14 Python
详解用Python爬虫获取百度企业信用中企业基本信息
2020/07/02 Python
Keras loss函数剖析
2020/07/06 Python
孕妇装中的著名品牌:Isabella Oliver(伊莎贝拉·奥利弗)
2016/10/31 全球购物
酒店前台接待岗位职责
2013/12/03 职场文书
婚纱摄影师求职信
2014/03/07 职场文书
《飘》英文读后感五篇
2019/10/11 职场文书
Mysql 8.x 创建用户以及授予权限的操作记录
2022/04/18 MySQL