input[type=file]
这个特殊的表单元素呢?如果不可以,那除了flash外,还有没有其他的方法可以实现异步提交文件呢?
很不幸地,如果只是使用平常我们用的XMLHttpRequest是不行的,因为它并不支持二进制文件的传输,但是注意,我说的是我们平常用的,也就是说还有我们很少用的或者说还未用的,那就是XmlHttpRequest Level 2,它是XMLHttpRequest的新版本,增加了请求时限,文件传输,跨域请求,进度事件等等新的特性。
下面就是一个简单的利用这些新特性实现的文件异步上传的例子:
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>Document</title>
- </head>
- <body>
- <input id="file" name="file" type="file">
- <button id="btn" type="button">Upload</button>
- <script src="http://cdn.bootcss.com/jquery/1.11.3/jquery.js"></script>
- <script>
- var $file = $('#file');
- $('#btn').click(function() {
- var data = new FormData();
- data.append('file', $file[0].files[0]);
- data.append('foo', 'bar');
- var xhr = new XMLHttpRequest();
- xhr.open('post', '/upload');
- xhr.onload = function(e) {
- alert(e.currentTarget.response);
- }
- xhr.send(data);
- });
- </script>
- </body>
- </html>
唯一令人遗憾的就是,IE9及其以下版本并不支持这些新特性,如果又想兼容IE的低版本怎么办?还好我们可以通过iframe来模拟实现,具体做法就是将input[type=file]
包裹上一层<form>
,并将它的target属性指向一个隐藏的iframe,通过提交form表单来模拟文件的异步上传,可以参考下面的代码:
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>Document</title>
- </head>
- <body>
- <input id="file" name="file" type="file">
- <button id="btn" type="button">Upload</button>
- <script src="http://cdn.bootcss.com/jquery/1.11.3/jquery.js"></script>
- <script>
- var $file = $('#file');
- $('#btn').click(function() {
- upload({
- url: '/upload',
- data: {
- foo: 'bar'
- },
- callback: function(res) {
- console.log(res);
- }
- })
- });
- function upload(opts) {
- var $iframe = $('#uploaderFrame');
- if ($iframe.length === 0) {
- $iframe = $('<iframe id="uploaderFrame" name="uploaderFrame" style="display:none;"></iframe>')
- .appendTo('body')
- .load(function() {
- var response, responseStr = $(this).contents().text();
- try {
- response = JSON.parse(responseStr);
- } catch (e) {
- response = responseStr;
- }
- $file.siblings().remove();
- $file.unwrap();
- opts.callback(response);
- });
- }
- $file.wrap('<form action="' + opts.url + '" method="post" enctype="multipart/form-data" target="uploaderFrame"></form>');
- var $from = $file.parent();
- for (var key in opts.data) {
- $from.append('<input type="hidden" name="' + key + '" value="' + opts.data[key] + '">');
- }
- $from.submit();
- }
- </script>
- </body>
- </html>
另外像jQuery.form.js已经完成了对我上面所说的逻辑的封装,可以直接拿来使用。唯一需要注意的是,如果在IE中使用这种方法来异步提交文件,并且服务器返回的response header的content-type
值为application/json
,那么IE就会弹出下载json文件的提示,解决的办法是将content-type
改为text/html
,再在js中通过JSON.parse()转化为对象。