最近工作中遇到上传文件问题,主要需求是一步点击上传,兼容ie8+,当时用的dojox/form/uploader控件,这两天扒了一下源码,明白了原理拿出来分享一下。
总体思路如下:
1、对于支持XMLHttpRequest2的浏览器使用FormData通过ajax上传
2、对于ie10一下的浏览器使用iframe异步上传,还需后台服务器做相应处理,这部分也是dojo/request/iframe上传文件的原理。
一、使用FormData上传文件
FormData最频繁使用的功能就是表单序列化及创建与表单格式相同的数据。append方法接收两个参数,字段名与字段值,字段值可以是File、Blob、String.
1 var data = newFormData(form);2 data.append("name", "woodtree");3 data.append(file.name, file);4 data.append(name, Blob);
如果直接向FormData的构造函数中传入表单元素,可以将表单元素的数据预先填入。
1 new FormData(document.forms[0])
FormData的另一个便利之处就是不用明确指定Content-Type头部,xhr对象能够根据FormData实例自动配置适当的头部。下面是一个简单的上传文件demo。
1
2
3
4
5
6
FormData7
8
9
10
11
12
13
14 varform=document.getElementById('uploader');15 varapp=document.getElementById('app');16 form.addEventListener('submit',function(evt) {17 evt.preventDefault();//组织页面刷新
18 vardata= newFormData();19 for(vari= 0, len=app.files.length; i
21 varfile=app.files[i];22 data.append(file.name, file);23 }24
25 varxhr= newXMLHttpRequest();26 xhr.οnlοad= function() {27 alert(JSON.parse(xhr.responseText).success);28 };29 xhr.οnerrοr= function(err) {30 console.error(err);31 };32 xhr.open('post','./upload',true);33 xhr.send(data);34 },false);35
36
37
View Code
server端代码使用formidable模块将文件暂存在tmp目录下。
1 var http = require('http');2 var url = require('url');3 var fs = require('fs');4 var qs = require('querystring');5 var request = require('request');6 var formidable = require('formidable');7
8 http.createServer(function(req, res){9 var _url =url.parse(req.url);10 if (_url.pathname === '/index') {11 fs.readFile('./index.html', function(err, data) {12 res.writeHead(200, {"Content-Type": "text/html; charset=UTF-8"});13 res.write(data);14 res.end();15 });16 } else if (_url.pathname === '/upload') {17 console.log(req.headers['content-type']);18 handle(req, res);19 }20 }).listen(8888);21 var handle = function(req, res) {22 if (req.headers['content-type'].indexOf('multipart/form-data') >= 0) {23 var formStream = newformidable.IncomingForm();24 formStream.uploadDir = './tmp';25 formStream.parse(req, function(err, fields, files) {26 res.writeHead(200, {"Content-Type": "application/json"});27 if(err) {28 res.write('{"success": false}');29 } else{30 res.write('{"success": true}');31 }32 res.end();33 });34 }35 }
View Code
查看请求,xhr自动为我们设置请求头部。
兼容性问题
二、使用iframe上传文件
兼容旧版本的ie浏览器实现无刷新上传,只能借由iframe来实现,大多数类库的做法是动态插入一个iframe元素,将form元素的target属性设置为新添加的iframe,这样只刷新了iframe的内容而避免页面跳转到form元素的action属性所指定的url。这里我们根据dojo/request/iframe模块的原理来实现上传文件。
该模块需要后台返回响应的格式来配合。将需要返回的信息放在`textarea`标签内。然后绑定iframe的load事件,通过`doc.getElementsByTagName('textarea')`取得textarea中的数据。
1
2
3
4 uploadInfo5
6
7
下面是简单的demo
ArcGIS Web Applicationvarupload=document.getElementById('placeholder');varuploader=document.getElementById('uploader');varapp=document.getElementsByName('app')[0];varclickLietener= function() {
app.click();
}varchangeListener= function() {
uploader.submit();
}if(app.addEventListener) {
app.addEventListener('change', changeListener,false);
}else if(app.attachEvent) {
app.attachEvent('onchange', changeListener);
}varappFrame=document.getElementById('frame');varlistener= function() {vardoc=appFrame.contentWindow.document;vartextAreas=doc.getElementsByTagName('textarea');if(textAreas&&textAreas.length> 0) {varresponse=textAreas[0].value;
alert(response);
}
}if(appFrame.addEventListener) {
appFrame.addEventListener('load',function(evt) {
listener();
},false);
}else if(appFrame.attachEvent) {
appFrame.attachEvent('onload',function() {
listener();
});
}