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 相关文章推荐
JavaScript开发时的五个注意事项
Dec 08 Javascript
js简单实现用户注册信息的校验代码
Nov 15 Javascript
简单的邮箱登陆的提示效果类似于yahoo邮箱
Feb 26 Javascript
alert出数组中的随即值代码
Sep 25 Javascript
浅析webapp框架AngularUI的demo
Dec 21 Javascript
JavaScript获取页面中第一个锚定文本的方法
Apr 03 Javascript
JQuery球队选择实例
May 18 Javascript
JavaScript实现打开链接页面的方式汇总
Jun 02 Javascript
Vue.js学习之过滤器详解
Jan 22 Javascript
利用node.js制作命令行工具方法教程(一)
Jun 22 Javascript
jQuery实现点击关注和取消功能
Jul 03 jQuery
详解gantt甘特图可拖拽、编辑(vue、react都可用 highcharts)
Nov 27 Vue.js
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
xajax写的留言本
2006/11/25 PHP
php 变量定义方法
2009/06/14 PHP
php设计模式 State (状态模式)
2011/06/26 PHP
php数组函数序列之array_keys() - 获取数组键名
2011/10/30 PHP
PHP 通过Socket收发十六进制数据的实现代码
2013/08/16 PHP
php实现微信公众平台发红包功能
2018/06/14 PHP
JavaScript 保存数组到Cookie的代码
2010/04/14 Javascript
JQuery优缺点分析说明
2010/06/09 Javascript
IE8下String的Trim()方法失效的解决方法
2013/11/08 Javascript
php的文件上传入门教程(实例讲解)
2014/04/10 Javascript
jQuery实现炫酷的鼠标轨迹特效
2015/02/01 Javascript
JavaScript中的this,call,apply使用及区别详解
2016/01/29 Javascript
微信开发 js实现tabs选项卡效果
2016/10/28 Javascript
ES6中Math对象新增的方法实例详解
2017/04/25 Javascript
去掉vue 中的代码规范检测两种方法(Eslint验证)
2018/03/21 Javascript
Vue高版本中一些新特性的使用详解
2018/09/25 Javascript
JavaScript事件冒泡机制原理实例解析
2020/01/14 Javascript
浅谈vue权限管理实现及流程
2020/04/23 Javascript
[23:18]Spirit vs Liquid Supermajor小组赛A组 BO3 第二场 6.2
2018/06/03 DOTA
Python 3.x读写csv文件中数字的方法示例
2017/08/29 Python
Python简单读取json文件功能示例
2017/11/30 Python
解决Python print 输出文本显示 gbk 编码错误问题
2018/07/13 Python
Python中类的创建和实例化操作示例
2019/02/27 Python
python画图——实现在图上标注上具体数值的方法
2019/07/08 Python
pytorch .detach() .detach_() 和 .data用于切断反向传播的实现
2019/12/27 Python
Python实现检测文件的MD5值来查找重复文件案例
2020/03/12 Python
python能在浏览器能运行吗
2020/06/17 Python
Python生成pdf目录书签的实例方法
2020/10/29 Python
python 利用openpyxl读取Excel表格中指定的行或列教程
2021/02/06 Python
纽约JewelryAffairs珠宝店:精细金银时尚首饰
2017/02/05 全球购物
Chemist Warehouse官方海外旗舰店:澳洲第一连锁大药房
2017/08/25 全球购物
雅诗兰黛加拿大官网:Estee Lauder加拿大
2019/07/31 全球购物
ellesse美国官方商店:意大利高级运动服品牌
2019/10/29 全球购物
捷克购买家具网站:JENA nábytek
2020/03/19 全球购物
2016优秀大学生个人事迹材料范文
2016/03/01 职场文书
php 解析非标准json、非规范json
2021/04/01 PHP