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 相关文章推荐
锋利的jQuery 要点归纳(三) jQuery中的事件和动画(上:事件篇)
Mar 24 Javascript
JQuery 动态扩展对象之另类视角
May 25 Javascript
JavaScript异步调用定时方法并停止该方法实现代码
Mar 16 Javascript
js解析与序列化json数据(一)json.stringify()的基本用法
Feb 01 Javascript
js实现简单的购物车有图有代码
May 26 Javascript
实例解析Array和String方法
Dec 14 Javascript
touch.js 拖动、缩放、旋转 (鼠标手势)功能代码
Feb 04 Javascript
Bootstrap笔记—折叠实例代码
Mar 13 Javascript
JavaScript设计模式之构造器模式(生成器模式)定义与用法实例分析
Jul 26 Javascript
webpack4 配置 ssr 环境遇到“document is not defined”
Oct 24 Javascript
javascript+css实现进度条效果
Mar 25 Javascript
微信小程序实现上传照片代码实例解析
Aug 04 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中获取远程客户端的真实ip地址的方法
2011/08/03 PHP
destoon会员注册提示“数据校验失败(2)”解决方法
2014/06/21 PHP
PHP chr()函数讲解
2019/02/11 PHP
如何解决PHP获取不到SESSION信息之一般情况
2019/10/10 PHP
使用tp框架和SQL语句查询数据表中的某字段包含某值
2019/10/18 PHP
jQuery ajax dataType值为text json探索分享
2013/09/23 Javascript
目前流行的JavaScript库的介绍及对比
2013/09/29 Javascript
jQuery Uploadify 上传插件出现Http Error 302 错误的解决办法
2015/12/12 Javascript
JavaScript数据绑定实现一个简单的 MVVM 库
2016/04/08 Javascript
JavaScript实现拖拽元素对齐到网格(每次移动固定距离)
2016/11/30 Javascript
微信小程序图片宽100%显示并且不变形
2017/06/21 Javascript
jQuery实现广告条滚动效果
2017/08/22 jQuery
浅谈js中的this问题
2017/08/31 Javascript
详解Vuex下Store的模块化拆分实践
2019/07/31 Javascript
layui 弹出删除确认界面的实例
2019/09/06 Javascript
[44:15]DOTA2上海特级锦标赛主赛事日 - 5 败者组决赛Liquid VS EG第二局
2016/03/06 DOTA
[07:48]DOTA2上海特级锦标赛主赛事首日RECAP
2016/03/04 DOTA
python使用新浪微博api上传图片到微博示例
2014/01/10 Python
python抓取最新博客内容并生成Rss
2015/05/17 Python
解析Mac OS下部署Pyhton的Django框架项目的过程
2016/05/03 Python
python使用arcpy.mapping模块批量出图
2017/03/06 Python
python验证码识别教程之灰度处理、二值化、降噪与tesserocr识别
2018/06/04 Python
python 拼接文件路径的方法
2018/10/23 Python
django2笔记之路由path语法的实现
2019/07/17 Python
css3一款3D字体带阴影效果的实现步骤
2013/03/20 HTML / CSS
美国维生素、补充剂、保健食品购物网站:Vitacost
2016/08/05 全球购物
Homestay中文官网:全球寄宿家庭
2018/10/18 全球购物
巴西电子、家电、智能手机购物网站:Girafa
2019/06/04 全球购物
应届生服装设计自我评价
2013/09/20 职场文书
学校卫生检查制度
2014/02/03 职场文书
《一个中国孩子的呼声》教学反思
2014/02/12 职场文书
万年牢教学反思
2014/02/15 职场文书
监察建议书格式
2014/05/19 职场文书
医院安全生产月活动总结
2014/07/05 职场文书
小班教师个人总结
2015/02/05 职场文书
Python 阶乘详解
2021/10/05 Python