使用iframe伪装异步上传文件,跨域上传文件
前言
一般地,文件上传有几种方式,第一种就是同步文件上传,可以使用传统的表单提交的方式,第二种就是异步文件上传的方式,如果要使用Ajax提交的话,对于一般地表单可以直接使用序列化的方式直接提交,但是对于文件格式只能使用H5的APIFormData
进行文件上传,但是对于一些旧的浏览器来说,并不支持H5,那么如何在旧的浏览器中实现文件的异步上传呢?这就是我们今天要说的一种解决方案,使用iframe伪装异步文件上传。
原理
对于表单元素form
,它具有一些如下的常见属性:
name
规定表单的名字enctype
规定表单的编码方式method
规定表单的发送数据方法action
规定发送的URL地址target
规定在何处打开action URL
然后我们在和表单的同一页面下,增加一个隐藏的iframe元素,让表单的target
指向这个隐藏的iframe
,跳转后的页面还是当前页面,就像没有刷新一样,然后后台返回的数据当前页面是iframe
,只需要获取iframe
的parent.document
即可获取原先的页面,就可以很方便地写入后台返回的信息了。
实现
前端页面iframe.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>iframe 文件上传</title>
</head>
<body>
<h2 id='info'></h2>
<form action='/uploadFile' method='POST' target='iframe' enctype='multipart/form-data'>
<!-- 通过tartget设置表单提交后跳转的地方-->
<input type='file' name='pic' />
<input type='submit' value='提交' />
</form>
<iframe id="iframe" name='iframe' width='0px' height='0px' frameborder='0'>
</iframe>
<!-- 将iframe的高宽,边框都设置为0,相当于隐藏了-->
<script type="text/javascript">
var iframe = document.getElementById("iframe");
var info = document.getElementById("info");
//Firefox/Opera/Safari中是iframe onload事件
//在IE下,是iframe onreadystatechange事件
iframe.onload = iframe.onreadystatechange = function(){
if (this.readyState && this.readyState != "complete") return;
else {
//获取iframe里面的内容
var responseText = iframe.contentDocument.body.textContent;
//上传完成后的处理
if(responseText!= ""){
info.innerHTML = responseText
}
}
}
</script>
</body>
</html>
后端Nodejs版本:
var http = require('http'),
fs = require("fs"),
formidable = require('formidable'),
port = 8080
http.createServer(function(req, res) {
res.setHeader("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
if (req.url == '/uploadFile') { //上传
var form = new formidable.IncomingForm(),
files = [],
fields = [];
form.uploadDir = 'upload/';
form
.on('field', function(field, value) {
fields.push([field, value]);
})
.on('file', function(field, file) {
files.push([field, file]);
fs.renameSync(file.path, 'upload/'+file.name);
})
.on('end', function() {
res.writeHead(200, {'content-type': 'text/plain'});
res.end("上传成功");
});
form.parse(req, function(err, fields, files) {
err && console.log('formidabel error : ' + err);
});
}else if(req.url == '/iframe.html'){ //发送html页面
fs.readFile('iframe.html', function (err, data) {
if (err) {
console.log(err);
// HTTP 状态码: 404 : NOT FOUND
// Content Type: text/plain
res.writeHead(404, {'Content-Type': 'text/html'});
}else{
// HTTP 状态码: 200 : OK
// Content Type: text/plain
res.writeHead(200, {'Content-Type': 'text/html'});
// 响应文件内容
res.write(data.toString());
}
// 发送响应数据
res.end();
});
} else {
res.writeHead(404, {'content-type': 'text/plain'});
res.end('404');
}
}).listen(port);
console.log('listening on http://localhost:'+port+'/');
跨域上传
上面的做法还是仅限于同域的情况,下面我们一起来学习下跨域情况下,该如何使用iframe来上传文件。
这时候我们还需要一个前端同域的中转页面,依然是前台提交请求后跳转到隐藏的iframe
,但是这次会带有一个参数,告诉后台需要重定向的地址,也就是我们的中转页面,后台重定向这个中转页面后,还会把上传后的消息通过一个参数附加在URL中。重定向的中转页面会在iframe
中显示,然后在中转页面中通过parent.document
获取上传页面的document
元素,就可以实现跨越上传了:
跨域上传的iframeCors.html
基本代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>iframe 文件上传</title>
</head>
<body>
<h2 id='info'></h2>
<form action='http://127.0.0.1:8081/uploadFile?cb=http://127.0.0.1:8080/proxy.html' method='POST' target='iframe' enctype='multipart/form-data'>
<!-- 通过tartget设置表单提交后跳转的地方-->
<input type='file' name='pic' />
<input type='submit' value='提交' />
</form>
<iframe id="iframe" name='iframe' width='0px' height='0px' frameborder='0'>
</iframe>
<!-- 将iframe的高宽,边框都设置为0,相当于隐藏了-->
</body>
</html>
中转页面proxy.html
基本代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>peoxy</title>
</head>
<body>
proxy
<script type="text/javascript">
var mes = window.location.href.split("mes=")[1];
parent.document.getElementById("info").innerHTML = mes
</script>>
</body>
</html>
后台代码:
var http = require('http'),
fs = require("fs"),
formidable = require('formidable'),
port = 8081
http.createServer(function(req, res) {
if (req.url.indexOf('/uploadFile')>=0) { //上传
var form = new formidable.IncomingForm(),
files = [],
fields = [];
form.uploadDir = 'upload/';
form
.on('field', function(field, value) {
fields.push([field, value]);
})
.on('file', function(field, file) {
files.push([field, file]);
fs.renameSync(file.path, 'upload/'+file.name);
})
.on('end', function() {
var proxy = req.url.split("cb=")[1]+"?mes=上传成功"
res.writeHead(301, {'Location': proxy});
res.end();
});
form.parse(req, function(err, fields, files) {
err && console.log('formidabel error : ' + err);
});
}else {
res.writeHead(404, {'content-type': 'text/plain'});
res.end('404');
}
}).listen(port);
console.log('listening on http://localhost:'+port+'/');