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 相关文章推荐
(currentStyle)javascript为何有时用style得不到已设定的CSS的属性
Aug 15 Javascript
[原创]来自ImageSee官方 JavaScript图片浏览器
Jan 16 Javascript
js 居中漂浮广告
Mar 21 Javascript
jquery禁止回车触发表单提交
Dec 12 Javascript
用模版生成HTML的的框架jquery.tmpl使用详解
Jan 07 Javascript
DOM节点删除函数removeChild()用法实例
Jan 12 Javascript
简单谈谈JavaScript的同步与异步
Dec 31 Javascript
JSON在Javascript中的使用(eval和JSON.parse的区别)详细解析
Sep 05 Javascript
vue获取dom元素注意事项
Dec 28 Javascript
Layui多选只有最后一个值的解决方法
Sep 02 Javascript
使用layui的layer组件做弹出层的例子
Sep 27 Javascript
解决vant title-active-color与title-inactive-color不生效问题
Nov 03 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压缩HTML函数轻松实现压缩html/js/Css及注意事项
2013/01/27 PHP
php 的反射详解及示例代码
2016/08/25 PHP
深入理解 PHP7 中全新的 zval 容器和引用计数机制
2018/10/15 PHP
基于PHP实现邮箱验证激活过程详解
2020/10/28 PHP
asp 的 分词实现代码
2007/05/24 Javascript
javascript setTimeout和setInterval 的区别
2009/12/08 Javascript
Jquery Ajax学习实例4 向WebService发出请求,返回实体对象的异步调用
2010/03/16 Javascript
JavaScript在IE和FF下的兼容性问题
2014/05/19 Javascript
JavaScript 学习笔记之语句
2015/01/14 Javascript
基于JavaScript实现百叶窗动画效果不只单纯flas可以实现
2016/02/29 Javascript
使用Web Uploader实现多文件上传
2016/06/08 Javascript
javascript弹出窗口中增加确定取消按钮
2016/06/24 Javascript
JS实现的模仿QQ头像资料卡显示与隐藏效果
2017/04/07 Javascript
20行JS代码实现网页刮刮乐效果
2017/06/23 Javascript
详解如何在vue项目中使用eslint+prettier格式化代码
2018/11/10 Javascript
vue自定义键盘信息、监听数据变化的方法示例【基于vm.$watch】
2019/03/16 Javascript
.netcore+vue 实现压缩文件下载功能
2020/09/24 Javascript
原生JS实现拖拽功能
2020/12/16 Javascript
Python使用pymysql从MySQL数据库中读出数据的方法
2018/07/25 Python
opencv python 基于KNN的手写体识别的实例
2018/08/03 Python
python分块读取大数据,避免内存不足的方法
2018/12/10 Python
selenium+python环境配置教程详解
2019/05/28 Python
pytorch-RNN进行回归曲线预测方式
2020/01/14 Python
Python Flask框架实现简单加法工具过程解析
2020/06/03 Python
Python+pyftpdlib实现局域网文件互传
2020/08/24 Python
浅析python函数式编程
2020/09/26 Python
python实现scrapy爬虫每天定时抓取数据的示例代码
2021/01/27 Python
Application Cache未缓存文件无法访问无法加载问题
2014/05/31 HTML / CSS
澳大利亚音乐商店:Bava’s Music City
2019/05/05 全球购物
彪马荷兰官网:PUMA荷兰
2019/05/08 全球购物
Roxy俄罗斯官方网站:冲浪和滑雪板的一切
2020/06/20 全球购物
下面代码从性能上考虑,有什么问题
2015/04/03 面试题
实习生自我鉴定
2013/12/12 职场文书
Redis 哨兵集群的实现
2021/06/18 Redis
Java中多线程下载图片并压缩能提高效率吗
2021/07/01 Java/Android
苹果电脑mac os中货币符号快捷输入
2022/02/17 杂记