叹息,我们回到这里。我可以轻松地在任何体面的浏览器上使用CORS直接将文件上传到我的AWS S3存储桶。但是(它即将到来),随着IE我不得不回到Iframes。轻松,设置一个隐藏的iframe,创建一个表单,将其目标设置为Iframe name / id,提交表单。如果上传成功,Iframe将被重定向到我指定的网址,我可以访问我需要的任何内容。但是如果发生错误,由于Iframe现在位于AWS域上,我将无法访问错误的XML内容。事实上,我甚至不知道发生了错误。
我在互联网上见过勇敢的人谈论托管一个html文件,在要上传文件的同一个桶上,然后使用postMessages路由Iframe内容,或者那种东西。
有人可以向我解释如何实现这个神话般的解决方案吗? Blueimp的jQuery文件上传程序似乎解决了这个问题,但是上帝的代码是如此jQueryified,我还没有得到它的要点。
答案 0 :(得分:19)
几乎所有关于jQuery文件上传插件iframe上传的内容都在Iframe Transport plugin内(以及支持result.html页面)。
作为简介,您可能希望在其Cross domain uploads Wiki页面上阅读其用户说明,特别是跨站点iframe传输上传部分。 (请注意,根据他们的Browser support页面,IE< 10不支持上传进度等细节,所以我不会考虑使用iframe传输,至少不需要付出太大的努力。)
(另外,我不相信任何使用文件上传插件的S3上传实现都可以访问文件上传错误的XML内容)
Iframe Transport插件为jQuery添加了一个新的Ajax“transport”方法,并不是特定于File Upload插件。您可能需要阅读jQuery.ajaxTransport()的文档,以了解jQuery为添加新传输提供的API。
我将尝试总结一下Iframe传输插件正在做什么,以及它如何与将文件上传到Amazon S3相关:
触发文件上传时,会调用send()
函数。这个功能:
创建隐藏的表单元素
使用src="javascript:false;"
创建iframe元素,并将load
事件处理程序绑定到iframe
将iframe附加到隐藏的表单,并将隐藏的表单附加到文档中。
创建iframe并加载其“页面”时,会调用其load
事件处理程序。处理程序:
从iframe中清除自己,并绑定另一个load
事件处理程序
配置隐藏表格:
表单的action
将是S3存储桶的URL
表单的target
设置为iframe,以便服务器响应加载到iframe中
其他字段,例如AWSAccessKeyId
已添加。具体而言,success_action_redirect
设置为您服务器上result.html的网址,例如http://example.org/result.html?%s
。
通常情况下,%s
令牌应该被服务器端代码替换为上传结果,但是对于S3,这可以通过代码用成功值进行硬编码,因为Amazon将仅重定向到此URL如果上传成功。
原始表单中的文件输入字段将移动到隐藏表单中,克隆字段保留在原始字段的位置
提交隐藏表格
将文件输入字段移回原始表单,替换克隆字段
文件上传到S3。如果成功,Amazon会将iframe重定向到success_action_redirect
网址。如果不成功,亚马逊将返回错误,该错误也会加载到iframe中。
调用iframe的load
事件处理程序。处理程序:
尝试保存对iframe的document
对象的引用。如果文件上传失败,则处理程序会保存undefined
。
使用成功代码和对iframe的document
对象(或undefined
)
删除隐藏的表单(和iframe)
在控制返回到您的代码之前,iframe的document
对象会传递到转换器(位于Iframe Transport plugin的底部),具体取决于您期望的数据类型。转换器从document
对象中提取该数据并将其返回(如果文件上载失败,则返回undefined
)到您的回调中。
调用您的回调(传递给jQuery.ajax()的success
和/或complete
)。插件始终返回成功代码,因此不会触发任何error
回调。
如果传递给回调的数据是您在success_action_redirect
中包含的值,则文件上传成功。如果数据为undefined
,则文件上传失败。
更新:如果错误XML页面与S3存储桶保持同一原点,那么加载到另一个iframe的S3存储桶中的另一个页面可以访问原始iframe的内容(因为它们是来自同一个起源)。如果您需要支持IE6 / 7,您的主页可以使用postMessage()
(或easyXDM的FlashTransport与第二个iframe进行通信。)
答案 1 :(得分:3)
使用没有FileReader或FormData支持的浏览器向用户提供准确反馈的问题让我很烦恼。我花了整整3天的时间试图想出一个解决方案,最后想出了一些几乎没有的东西。
让我们了解事实:
好的,没有其他方法可以上传文件而不是使用iframe。正确?
因此,使用jQuery Iframe Transport作为@jeferry_to的jQuery文件上传描述了这项工作的工具。
*实际上工具/插件不会改变一件事..
现在怎么办?
嗯......我们需要在传输iframe中访问S3响应。但我们不能因为它在不同的领域。 因此我们决定使用涉及第二个iframe的技巧来处理它。
设置:
情景:
首先,我们需要修改jQuery Iframe Transport,以便它不会自动删除自动生成的表单和传输帧。我们需要这样做,因为稍后使用的#postMessage本质上是异步的,我们不希望iframe在我们尝试访问它时消失。
top.frames['iframe X'].document.documentElement
之类的操作来访问TransportFrame的te内容,将它们字符串化并通过#postMessage将它们发送回TopFrame。好的,现在一切都应该有效,因为本书完成了一切。
Nahh,你甚至不应该打扰。
你看......如果你强迫现代浏览器使用iframe传输而不是XHR2,上面的解决方案确实会像魅力一样工作。
然而这毫无意义。我们希望它能在IE8 + 9中运行。
嗯......在IE8 / 9中,它有时会起作用,有时则不然。通常它没有。
为什么呢?由于IE的友好HTTP错误消息。哦,是的,你读得很好。
如果出现错误,S3会根据错误(400,403等)响应HTTP错误状态。现在,根据显示的响应的状态和长度here,IE会丢弃S3响应并用友好的错误消息替换它。为了克服这个问题,您必须确保响应总是> 512字节。在这种情况下,您无法保证这样的事情,因为您无法控制响应。 S3的确如此,典型的错误小于512字节。
简而言之:
iframe技巧适用于那些不需要它的浏览器,而不适用于那些不需要它的浏览器。
不幸的是,我无法想到其他任何事情,所以现在我已经关闭了案件。
答案 2 :(得分:1)
总结我在评论中的答案:IE有一些限制的CORS支持:http://www.html5rocks.com/en/tutorials/cors/
这种直接上传到S3的实现看起来比jquery fileupload简单得多,而且不在jquery中: http://codeartists.com/post/36892733572/how-to-directly-upload-files-to-amazon-s3-from-your
希望这有帮助!
答案 3 :(得分:1)
AS为“postMessage”场景,也许iframe应该包含一个简单的javascript
[edit]用于错误消息接管的iframe
IFRAME脚本
window.document.onload = function(e){
window.parent.postMessage(document, '*'); //replace '*' with your parent if possible
}
// just to get the proper document for the parent to target me
window.addEventListener('message',function(e) {
if (e.domain == 'example.com') { // the domain of your parent frame
if (e.data == "Salute") {
window.parent.postMessage("I'm here", '*'); //replace '*' with your parent too
}
}
});
现在,父母非常了解iFrame并且可以跟踪它的状态(取决于它是否回答了一个简单的postMessage)
父母脚本
var iFrameTarget;
var iFrameTakenOver = false;
var timer;
window.addEventListener('message',function(e) {
if (e.domain == 'example.com') { // the domain of your iframe
if (e.data) { // e.data contains the iframe document
if(typeof(e.data) =='object')
iFrameTarget = e.source;
elseif(e.data == "I'm here")
{
iFrameTakenOver = false;
}
timer =setInterval(call_iFrame(),5000); // check iFrame presence in 5 seconds
}
}
});
function call_iFrame() {
iFrameTarget.postMessage('Salute');
iFrameTakenOver = true;
}
如果iframe没有响应它的“代码”,iFrameTakenOver将永久设置为false检查,以验证是否发生了错误。