我成功地将文件从localhost:8888
发送到localhost:8080
(生产中的不同域(,但是传输完成后无法读取HTTP响应。
为了未捕获的安全错误:无法读取"内容文档"属性 来自"HTMLIFrameElement":阻止了具有源的框架 "http://localhost:8888"访问具有原点的帧 "http://localhost:8080"。请求访问集的框架 "document.domain"到"localhost",但正在访问的框架确实 不。两者都必须将"document.domain"设置为相同的值才能允许 访问。
发送文件,为了兼容性支持,我正在尝试让它适用于基于<form>
的文件上传; 而不是基于XHR
。这是基本的 HTML 结构:
<form target="file-iframe" enctype="multipart/form-data" method="POST" action="invalid">
<input type="file" id="file-input" class="file-input" title="select files">
</form>
<iframe src="javascript:false;" id="file-iframe" name="file-iframe"></iframe>
要将<iframe>
元素插入 DOM 中,我执行以下操作:
document.domain = document.domain;
var domainHack = 'javascript:document.write("<script type=text/javascript>document.domain=document.domain;</script>")';
var html = '<iframe id="file-iframe" name="file-iframe"></iframe>';
var parent = document.getElementById('wrapper');
var iframe = UTILS.createDomElement(html, parent);
iframe.src = domainHack;
UTILS.attachEvent(iframe, 'load', function(e) {
// this throws the above SecurityError
var doc = iframe.contentDocument || iframe.contentWindow.document;
// do other stuff...
});
在提交表单之前,我将<form>
上的action
属性设置为目标跨域网址:
action="http://localhost:8080/"
提交<form>
后,<iframe>
的load
事件被触发,我尝试访问<iframe>
的内容以读取HTTP响应。但是,这样做会引发上述错误,因为这是一个跨源请求,我无法访问<iframe>
的内容。
我以为document.domain
黑客会起作用,但错误消息告诉我iframe
没有将域设置为 localhost
,即使我将iframe
的 src
属性设置为 domainHack
变量,它似乎执行。
关于我可能做错了什么的任何想法?如何将document.domain
设置为<iframe>
及其父页面(即当前页面(localhost
。
我已经阅读了几个StackOverflow问题,一些MDN文章,以及Google上的其他随机结果,但我无法做到这一点。我已经看过的一些东西了:
- 规避同源策略的方法
- https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#Changing_origin
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
在四处挖掘并试图解决这个问题之后,我终于遇到了一个似乎对我有用的解决方案。但是,这不是我问题的确切答案。
总之,我正在努力支持基于<form>
的文件上传。对于不支持通过XHR
上传文件的浏览器,我们必须诉诸传统的<form>
提交,使用隐藏<iframe>
来避免页面刷新。表单将重新加载操作重定向到隐藏<iframe>
,然后在文件传输后将HTTP响应写入<iframe>
的正文。
由于同源政策,因此我问这个问题的原因,我无法访问<iframe>
的内容。<iframe>
的同源策略限制从一个源加载的文档或脚本与另一个源的资源交互的方式。由于我们无法访问<iframe>
的文档,这是文件上传HTTP响应被写入的地方,服务器将返回一个重定向响应,服务器将向其附加上传响应JSON。当<iframe>
加载时,JS将解析出响应JSON,并将其写入<iframe>
的主体。最后,由于重定向到同一来源,我们可以访问<iframe>
的内容:)
非常感谢jQuery文件上传器;他们做了所有的艰苦工作;)
https://github.com/blueimp/jQuery-File-Upload/wiki/Cross-domain-uploads
建立。。。
.JS
function setupForm() {
// form is declared outside of this scope
form = document.createElement('form');
form.setAttribute('id', 'upload-form');
form.setAttribute('target', 'target-iframe');
form.setAttribute('enctype', 'multipart/form-data');
form.setAttribute('method', 'POST');
// set the 'action' attribute before submitting the form
form.setAttribute('action', 'invalid');
};
function setupIframe() {
// iframe is declared outside of this scope
iframe = document.createElement('iframe');
/*
* iframe needs to have the 'name' attribute set so that some versions of
* IE and Firefox 3.6 don't open a new window/tab
*/
iframe.id = 'target-iframe';
iframe.name = 'target-iframe';
/*
* "javascript:false" as initial iframe src to prevent warning popups on
* HTTPS in IE6
*/
iframe.src = 'javascript:false;';
iframe.style.display = 'none';
$(iframe).bind('load', function() {
$(iframe)
.unbind('load')
.bind('load', function() {
try {
/*
* the HTTP response will have been written to the body of the iframe.
* we're assuming the server appended the response JSON to the URL,
* and did the redirect correctly
*/
var content = $(iframe).contents().find("body").html();
response = $.parseJSON(content);
if (!response) {
// handle error
return;
}
uploadFile(...); // upload the next file
}
catch (e) {
// handle error
}
});
});
/*
* insert the iframe as a sibling to the form. I don't think it really
* matters where the iframe is on the page
*/
$(form).after(iframe);
};
HTML - 重定向页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
</head>
<body>
<script type="text/javascript">
// grabs the JSON from the end of the URL
document.body.innerHTML = decodeURIComponent(window.location.search.slice(1));
</script>
</body>
</html>
剩下要做的就是将<form>
上的 action
属性设置为我们要向其发送上传的跨域 URL:
form.setAttribute('action', 'http://sub.example.com:8080/upload?id=ab123');
form.submit();
同时,在服务器上...
// send redirect to the iframe redirect page, where the above HTML lives
// generates URL: http://example.com/hack?%7B%22error%22%3Afalse%2C%22status%22%3A%22success%22%7D
response.sendRedirect("http://example.com/hack?{"error":false,"status":"success"}");
我知道这是一个绕过<iframe>
同源策略的巨大黑客,但它似乎有效,而且我认为它在跨浏览器兼容性方面非常好。我还没有在所有浏览器中测试过它,但我会开始这样做,并发布更新。
好吧,我也来了解决方案。它有点硬编码的ATM,但看起来仍然很整洁。
首先,我在服务器上制作了一个 html 文件。(稍后我会将其修改为 ejs 模板,其中包括我的数据(。
<!DOCTYPE html>
<html>
<title>Page Title</title>
<script>
function myFunction() {
parent.postMessage('Some message!!!', 'http://192.168.0.105:3001'); // hard coded, will change this later
}
window.onload=myFunction;
</script>
<body>
</body>
</html>
这里重要的部分是父级的使用。
而不是从我的节点服务器上传文件并将 html 文件发送回客户端:
res.sendFile('file.html');
在客户端上,我有和你一样的html。
'<form id="{id}_form" action="http://192.168.0.105:3011/private/profile_picture/upload" enctype="multipart/form-data" method="post" target="{id}_uploadframe">',
'<span id="{id}_wrapper" class="file-wrapper">',
'<input id="{id}_real" type="file" accept="image/*" name="photo" />',
'<span class="button">{0}</span>',
'</span>',
'</form>',
'<iframe id="{id}_uploadframe" name="{id}_uploadframe" class="mc-hidden"></iframe>',
我在页面上呈现了这个模板。我还添加了以下事件处理程序
window.addEventListener('message',function(event) {
//if(event.origin !== cross_domain) return;
console.log('message received: ' + event.data,event);
},false);
如您所知,addEventListener 并非适用于所有浏览器。并且此解决方案不适用于不支持postMessage的IE8<8。希望这对你来说是幸福的