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 相关文章推荐
toString()一个会自动调用的方法
Feb 08 Javascript
ExtJS4 表格的嵌套 rowExpander应用
May 02 Javascript
15个jquery常用方法、小技巧分享
Jan 13 Javascript
javascript函数式编程实例分析
Apr 25 Javascript
vue.js表格分页示例
Oct 18 Javascript
Bootstrap图片轮播组件Carousel使用方法详解
Oct 20 Javascript
jquery配合.NET实现点击指定绑定数据并且能够一键下载
Oct 28 Javascript
利用CSS、JavaScript及Ajax实现图片预加载的方法
Nov 29 Javascript
javascript 的变量、作用域和内存问题
Apr 19 Javascript
vue.js 实现输入框动态添加功能
Jun 25 Javascript
javascript实现fetch请求返回的统一拦截
Dec 22 Javascript
es6数组之扩展运算符操作实例分析
Apr 25 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程序实现支持页面后退的两种方法
2008/06/30 PHP
php数据类型判断函数有哪些
2013/09/23 PHP
PHP中echo,print_r与var_dump区别分析
2014/09/29 PHP
PHP解压ZIP文件到指定文件夹的方法
2016/11/17 PHP
JAVASCRIPT keycode总结
2009/02/04 Javascript
封装好的js判断操作系统与浏览器代码分享
2015/01/09 Javascript
PHP守护进程实例
2015/03/06 Javascript
javascript实现英文首字母大写
2015/04/23 Javascript
js实现按钮颜色渐变动画效果
2015/08/20 Javascript
简单实现限制uploadify上传个数
2015/11/16 Javascript
jQuery简单获取键盘事件的方法
2016/01/22 Javascript
JS、jQuery中select的用法详解
2016/04/21 Javascript
js实现登录框鼠标拖拽效果
2017/03/09 Javascript
Angular2搜索和重置按钮过场动画
2017/05/24 Javascript
详解nodejs异步I/O和事件循环
2017/06/07 NodeJs
React Native如何消除启动时白屏的方法
2017/08/08 Javascript
vuedraggable+element ui实现页面控件拖拽排序效果
2020/07/29 Javascript
Vue 嵌套路由使用总结(推荐)
2020/01/13 Javascript
vue简单封装axios插件和接口的统一管理操作示例
2020/02/02 Javascript
Python字符转换
2008/09/06 Python
Python map和reduce函数用法示例
2015/02/26 Python
详解python并发获取snmp信息及性能测试
2017/03/27 Python
CentOS 7下安装Python 3.5并与Python2.7兼容并存详解
2017/07/07 Python
Pycharm技巧之代码跳转该如何回退
2017/07/16 Python
python切片及sys.argv[]用法详解
2018/05/25 Python
利用Python如何批量更新服务器文件
2018/07/29 Python
python使用folium库绘制地图点击框
2018/09/21 Python
django使用LDAP验证的方法示例
2018/12/10 Python
python使用knn实现特征向量分类
2018/12/26 Python
Django框架models使用group by详解
2020/03/11 Python
Matplotlib 绘制饼图解决文字重叠的方法
2020/07/24 Python
浅析NumPy 切片和索引
2020/09/02 Python
什么是静态路由,其特点是什么?什么是动态路由,其特点是什么?
2013/07/26 面试题
书法比赛获奖感言
2014/02/10 职场文书
竞争上岗实施方案
2014/03/21 职场文书
2014年财政工作总结
2014/12/10 职场文书