1 var data = new FormData(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 <!doctype html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no"> 6 <title>FormData</title> 7 </head> 8 <body> 9 <form id="uploader" action="/upload" enctype="multipart/form-data"> 10 <input id="app" type="file" multiple> 11 <input type="submit" value="Submit"> 12 </form> 13 <script> 14 var form = document.getElementById('uploader'); 15 var app = document.getElementById('app'); 16 form.addEventListener('submit', function(evt) { 17 evt.preventDefault();//組織頁面刷新 18 var data = new FormData(); 19 for (var i = 0, len = app.files.length; i < len; i++) { 20 //file property: name, size, type, lastModifiedDate 21 var file = app.files[i]; 22 data.append(file.name, file); 23 } 24 25 var xhr = new XMLHttpRequest(); 26 xhr.onload = function() { 27 alert(JSON.parse(xhr.responseText).success); 28 }; 29 xhr.onerror = function(err) { 30 console.error(err); 31 }; 32 xhr.open('post', './upload', true); 33 xhr.send(data); 34 }, false); 35 </script> 36 </body> 37 </html>
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 = new formidable.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 }
查看請求,xhr自動為我們設置請求頭部。
兼容性問題
1 <html> 2 <body> 3 <textarea> 4 uploadInfo 5 </textarea> 6 </body> 7 </html>
下面是簡單的demo

<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <meta http-equiv="X-UA-Compatible" content="IE=EDGE" /> <title>ArcGIS Web Application</title> </head> <body class="claro"> <form id="uploader" method="post" action="/upload" target="appFrame" encoding="multipart/form-data" enctype="multipart/form-data"> <input id="appInput" name="app" type="file" > </form> <iframe id="frame" name="appFrame" src="" style="visibility:hidden;"></iframe> <script type="text/javascript"> var upload = document.getElementById('placeholder'); var uploader = document.getElementById('uploader'); var app = document.getElementsByName('app')[0]; var clickLietener = function() { app.click(); } var changeListener = function() { uploader.submit(); } if (app.addEventListener) { app.addEventListener('change', changeListener, false); } else if (app.attachEvent) { app.attachEvent('onchange', changeListener); } var appFrame = document.getElementById('frame'); var listener = function() { var doc = appFrame.contentWindow.document; var textAreas = doc.getElementsByTagName('textarea'); if (textAreas && textAreas.length > 0) { var response = textAreas[0].value; alert(response); } } if (appFrame.addEventListener) { appFrame.addEventListener('load', function(evt) { listener(); }, false); } else if(appFrame.attachEvent) { appFrame.attachEvent('onload', function() { listener(); }); } </script> </body> </html>

1 var http = require('http'); 2 var url = require('url'); 3 var fs = require('fs'); 4 var qs = require('querystring'); 5 var formidable = require('formidable'); 6 7 http.createServer(function(req, res) { 8 var _url = url.parse(req.url); 9 if (_url.pathname === '/index') { 10 fs.readFile('./index.html', function(err, data) { 11 res.writeHead(200, { 12 "Content-Type": "text/html; charset=UTF-8" 13 }); 14 res.write(data); 15 res.end(); 16 }); 17 } else if (_url.pathname === '/upload') { 18 var formStream = new formidable.IncomingForm(); 19 formStream.uploadDir = './tmp'; 20 formStream.parse(req, function(err, fields, files) { 21 console.log(fields); 22 console.log(files); 23 var info = null; 24 var accept = req.headers.accept; 25 if (err) { 26 info = {success: false}; 27 } else { 28 info = {success: true}; 29 } 30 if (accept.indexOf('application/json') > -1) { 31 res.writeHead(200, { 32 "Content-Type": "application/json;charset=utf-8" 33 }); 34 res.write(JSON.stringify(info)); 35 } else { 36 res.writeHead(200, { 37 "Content-Type": "text/html; charset=UTF-8" 38 }); 39 var responseText = '<html><body><textarea>' + 40 JSON.stringify(info) + 41 '</textarea></body></html>'; 42 res.write(responseText); 43 } 44 res.end(); 45 }); 46 } 47 }).listen(8888);
后台代碼需要注意Content-Type響應頭的設置,ie8、9碰到不知如何渲染的MIME類型會把它當成文件下載下來。這里和這里
不知大家有沒有注意到,上面的demo是一步上傳,選擇好文件后直接上傳到服務器,ie8以上的瀏覽器沒問題,如果是在ie8中情況就有些棘手。ie中文件上傳控件長成這個樣子,單擊一下button會彈出文件選擇框,如果單擊的是text部分,沒有反映,你需要雙擊才會彈出選擇框。一個辦法是讓鼠標盡量單擊button部分,button的大小跟font-size有關。但如果你的可點擊區域太大。。。。。
所幸還是有解決辦法的,這時需要在form中加一個label標簽,for屬性指向file。這樣點擊label時會觸發for指向元素的click事件,這時label的自然行為。同時把file移除屏幕外。注意一定不能用input[type=button],在點擊button時候調用file的click事件,然后在file change事件中調用form.submit方法,這種行為在ie中是被禁止的,回報“access denied”錯誤。

1 <!DOCTYPE HTML> 2 <html> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> 6 <meta http-equiv="X-UA-Compatible" content="IE=EDGE" /> 7 <title>ArcGIS Web Application</title> 8 </head> 9 <body class="claro"> 10 <form id="uploader" method="post" action="/upload" target="appFrame" encoding="multipart/form-data" enctype="multipart/form-data"> 11 <label id="placeholder" for="appInput">upload</label> 12 <input id="appInput" name="app" type="file" style="position:absolute;left:-800px;"> 13 </form> 14 <iframe id="frame" name="appFrame" src="" style="visibility:hidden;"></iframe> 15 <script type="text/javascript"> 16 var upload = document.getElementById('placeholder'); 17 var uploader = document.getElementById('uploader'); 18 var app = document.getElementsByName('app')[0]; 19 var changeListener = function() { 20 uploader.submit(); 21 } 22 if (app.addEventListener) { 23 app.addEventListener('change', changeListener, false); 24 } else if (app.attachEvent) { 25 app.attachEvent('onchange', changeListener); 26 } 27 var appFrame = document.getElementById('frame'); 28 var listener = function() { 29 var doc = appFrame.contentWindow.document; 30 var textAreas = doc.getElementsByTagName('textarea'); 31 if (textAreas && textAreas.length > 0) { 32 var response = textAreas[0].value; 33 alert(response); 34 } 35 } 36 if (appFrame.addEventListener) { 37 appFrame.addEventListener('load', function(evt) { 38 listener(); 39 }, false); 40 } else if(appFrame.attachEvent) { 41 appFrame.attachEvent('onload', function() { 42 listener(); 43 }); 44 } 45 46 </script> 47 </body> 48 </html>
參考資料