是否有可能执行asynchronous跨域file upload?

有可能的! 参见下文。


首先,让我使用这个图解释如何实现asynchronousfile upload


抱歉。 我已经closures了我的一个域名,现在形象已经消失了。 这是一个非常好的形象,但。 这是之前我发现堆栈溢出通过Imgur上传图像。


正如你所看到的,诀窍是让HTTP响应加载到一个隐藏的IFRAME元素而不是页面本身。 (这是通过在使用JavaScript提交FORM时设置FORM元素的target属性来完成的。)

这工作。 但是,我面临的问题是服务器端脚本在不同的域上 。 FORM-submit是一个跨域HTTP请求。 现在,服务器端脚本已经启用CORS,这使得我的网页有权读取从我的页面到该脚本的HTTP请求的响应数据 – 但是,只有当我通过Ajax接收HTTP响应时才有效, ergo,JavaScript。

但是,在这种情况下,响应是针对IFRAME元素的。 一旦XML响应登陆到IFRAME,它的URL就是删除脚本 – 例如http://remote-domain.com/script.pl

不幸的是,CORS不包括这种情况(至less我认为) – 我无法读取IFRAME的内容,因为它的URL不匹配页面的URL(不同的域)。 我得到这个错误:

不安全的JavaScript尝试访问来自具有URL hxxp://my-domain.com/outer.html的框架的URL hxxp://remote-domain.com/script.pl。 域,协议和端口必须匹配。

而且由于IFRAME的内容是一个XML文档,所以IFRAME里面没有可以使用postMessage东西。

所以我的问题是: 如何从IFRAME获取XML内容?

正如我上面所说,我能够直接检索跨域的HTTP响应(CORS启用),但似乎我无法读取跨域HTTP响应,一旦他们加载到IFRAME。

就好像这个问题不能解决不了,让我排除这些解决scheme

  1. easyXDM和类似的技术需要在远程域上的一个端点,

  2. 更改XML响应(包含SCRIPT元素),

  3. 服务器端代理 – 我知道我可以有一个服务器端脚本在我的域可以作为代理。

那么,除了这两个解决scheme之外,可以这样做吗?


可以办到!!

事实certificate,可以伪造一个模仿multipart/form-data FORM提交的XHR请求(Ajax请求)(在上面的图像中使用它来将file upload到服务器)。

诀窍是使用FormData构造函数 – 阅读这篇Mozilla黑客文章获取更多信息。

这是你如何做到的:

 // STEP 1 // retrieve a reference to the file // <input type="file"> elements have a "files" property var file = input.files[0]; // STEP 2 // create a FormData instance, and append the file to it var fd = new FormData(); fd.append('file', file); // STEP 3 // send the FormData instance with the XHR object var xhr = new XMLHttpRequest(); xhr.open('POST', 'http://remote-domain.com/script.pl', true); xhr.onreadystatechange = responseHandler; xhr.send(fd); 

上面的方法执行一个asynchronous的file upload,相当于上图中描述的常规file upload,通过提交这个表单来实现:

 <form action="http://remote-domain.com/script.pl" enctype="multipart/form-data" method="post"> <input type="file" name="file"> </form> 

像一个老板一样 :)

只需发送一个跨域XHR请求与表单中的数据,而不是提交表单。 CORS只适用于前者。

如果您必须以其他方式进行操作,请使用postMessage与框架进行协商。

而且由于IFRAME的内容是一个XML文档,所以IFRAME里面没有可以使用postMessage的东西。

这是怎么阻止你的? 在HTML或SVG名称空间( <script xmlns="http://www.w3.org/1999/xhtml" type="application/ecmascript" src="..."/> )下包含脚本元素XML。

我想这不能用你描述的方式来完成。 通常情况下,如果您有跨域问题,您可以通过JSONp方法解决,但只适用于GET请求。 使用HTML5,你可能会发送GET请求的二进制文件,但这只是如果。

  • 解决办法是通过在本地Web服务器上代理请求来使本地Web服务可用。 这将导致您的本地Web服务器的额外负载,所以我可以想象它是不可行的。 如果这些文件很小而且很less,那么这样做会很好。

  • 另一种解决scheme是在发送文件后开始轮询服务器。 您可以发送一个令牌并使用常规JSONp轮询服务器的状态。 这样你就不需要从iframe中读取。

  • 将整个页面放在远程服务器上运行的iframe中。 这可能只是解决了这个问题,但是如果XML输出是某个过程的最后一步,那么这是非常可行的。

我相信你有一个很好的理由,处理服务器在不同的域名,但如果不是你不会有所有这些问题。 也许值得重新考虑?

如果可以的话,返回一个HTML页面而不是XML。
在该页面中,您可以在SCRIPT标签中使用以下命令: parent.postMessage

如果您必须支持旧浏览器(主要是<IE8),则可以为2Mb以下的消息编写并读取window.name

这两种技术都允许您在不同域的帧之间传递string数据。

另一种方法是使用setInterval ,它将使用JSONP从父页面反复调用远程域以了解状态。

无论如何,您将需要从远程域的合作来获取数据。

以下方法在我的设置(Firefox 3.6)中工作:

 <!-- hidden target frame --> <iframe name="load_target" id="load_target" onload="process(this);" src="#" ...> <!-- get data from iframe after load and process them --> <script type="text/javascript"> function process(iframe) { var data = iframe.contentWindow.document.body.innerHTML; // got test data="<xml><a>b</a></xml>" } </script> 

它也在Chrome中工作,但需要在加载父页面后排除第一个onload调用。 这很容易通过设置在process()testing的“全局”variables来实现。

加成

该方法与表单一起工作

 <form action="URL" method="post" enctype="multipart/form-data" target="load_target"> 

这是提交给URL 。 该URL需要与父网页page.html位于同page.html域。 如果要下载REMOTE_URL数据,那么URL将是一个PHP proxy.php

 <?php echo file_get_contents("REMOTE_URL"); ?> 

这是一个简单的方法 – 然而,它可能被排除在条件(2)的问题。 我已经在这里添加它来完成我的答案。

Mahemoff和Georges Auberger讨论了其他方法,仅考虑iframe。