通过 iframe 上传 Amazon S3

Posted

技术标签:

【中文标题】通过 iframe 上传 Amazon S3【英文标题】:Amazon S3 upload via Iframes 【发布时间】:2013-01-12 18:20:19 【问题描述】:

叹息,我们又回到了这个话题。我可以在任何足够体面的浏览器上轻松使用 CORS 直接将文件上传到我的 AWS S3 存储桶。但是(它来了),有了 IE,我不得不退回到 iframe。很简单,设置一个隐藏的 iframe,创建一个表单,将其目标设置为 iframe 名称/id,提交表单。如果上传成功,Iframe 将被重定向到我指定的 url,我可以访问我需要的任何内容。但如果发生错误,由于 iframe 现在位于 AWS 域中,我将无法访问错误的 XML 内容。事实上,我什至不知道发生了错误。

我在互联网上看到勇敢的人谈论托管一个 html 文件,在要上传文件的同一个存储桶上,然后使用 postMessages 来路由 iframe 内容,或类似的东西。

有人可以向我解释如何实现这个神话般的解决方案吗? Blueimp 的 jQuery 文件上传器似乎解决了这个问题,但上帝的代码是如此 jQueryified,以至于我无法理解它的要点。

为清晰而编辑

    IE 因为 1,我不能使用 XDomainRequest 向 S3 发送文件 所以使用 Iframe 并将其与完整的表单一起发布到 S3 如果成功,AWS 会重定向到服务器上的一个页面,该页面会读取标头并 然后返回一个 JSONP 样式的响应,客户端上的脚本可以读取该响应(重定向页面可以 由我指定)。 如果出错,我现在能做的就是等待超时到期,然后控制台日志 一个 IFRAME ID,并弹出一个警报,以便用户可以按 ID 查询 iframe, 阅读 DAMN XML 内容,找出 AWS 指定的错误,然后重试(我在讽刺......)

【问题讨论】:

为什么 CORS 不适用于 IE?我有一个使用 jquery fileupload 插件,但我没有在 IE 上测试过。这个帖子blog.appharbor.com/2013/01/10/…似乎表明它可以在IE上运行 @d33pika - 它使用神奇的 jQuery 文件上传插件。我在问题中提到,从插件中提取这么多逻辑的任务非常非常非常困难 @d33pika 还有IE中NO CORS的问题 @JibiAbraham:嗯,IE 从第 8 版开始就有 CORS,但按照传统,它显然决定走自己的路。 XDomainRequest - Restrictions, Limitations and Workarounds 基本插件并不复杂:github.com/blueimp/jQuery-File-Upload/wiki/Basic-plugin。看看吧。 【参考方案1】:

关于 jQuery File Upload 插件如何进行 iframe 上传,您需要了解的几乎所有内容都在其 Iframe Transport plugin(以及支持 result.html 页面)中。

作为介绍,您可能需要阅读他们在Cross domain uploads wiki 页面上的用户说明,特别是跨站点 iframe 传输上传部分。 (请注意,根据他们的Browser support 页面,IE

(另外,我不相信任何使用文件上传插件的 S3 上传实现都可以访问文件上传错误的 XML 内容)

Iframe 传输插件为 jQuery 添加了一个新的 Ajax“传输”方法,并且不特定于文件上传插件。您可能需要阅读 jQuery.ajaxTransport() 的文档以了解 jQuery 提供的用于添加新传输的 API。


我将尝试总结 Iframe Transport 插件的作用,以及它与将文件上传到 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 的 URL,例如http://example.org/result.html?%s.

    通常情况下,%s 令牌应由服务器端代码替换为上传结果,但在 S3 中,您的代码可以将其硬编码为成功值,因为亚马逊只会在以下情况下重定向到此 URL上传成功。

    原始表单中的文件输入字段被移动到隐藏表单中,克隆的字段留在原始字段的位置

    提交隐藏表单

    将文件输入字段移回原始表单,替换克隆的字段

    文件已上传到 S3。如果成功,亚马逊会将 iframe 重定向到 success_action_redirect URL。如果不成功,亚马逊会返回一个错误,该错误也会加载到 iframe 中。

    调用 iframe 的 load 事件处理程序。处理程序:

    尝试保存对 iframe 的 document 对象的引用。如果文件上传失败,处理程序将保存undefined

    使用成功代码和对 iframe 的 document 对象(或 undefined)的引用调用完整回调

    移除隐藏的表单(和 iframe)

    在将控制权返回给您的代码之前,iframe 的document 对象将传递给转换器(位于Iframe Transport plugin 的底部),具体取决于您期望的数据类型。转换器从document 对象中提取该数据并将其(或undefined,如果文件上传失败)返回给您的回调。

    您的回调(success 和/或 complete 传递给 jQuery.ajax())被调用。插件总是返回成功码,所以不会触发任何error回调。

    如果传递给您的回调的数据是您包含在success_action_redirect 中的值,则文件上传成功。如果数据为undefined,则文件上传失败。


更新:如果错误 XML 页面与 S3 存储桶位于同一来源,则 S3 存储桶中的另一个页面加载到另一个 iframe 中,可以访问原始 iframe 的内容(因为它们是同源)。您的主页可以使用postMessage()(或easyXDM 的FlashTransport,如果您需要支持IE6/7)与第二个iframe 通信。

【讨论】:

“尝试保存对 iframe 文档对象的引用”——这会引发无法捕获的错误吗? 错误是catchable。 我会试一试,除了错误处理之外,我已经准备好了一切,会告诉你的,tx【参考方案2】:

向使用不支持 FileReader 或 FormData 的浏览器的用户提供准确反馈的问题也困扰了我很多。我花了整整 3 天的时间试图想出一个解决方案,最后想出了一个几乎没有的东西。

让我们来看看事实:

Browser: IE8/9 // 成为任何其他不支持 FileReader 的浏览器的可能性很小 上传行为:出于用户体验的原因,我们需要它是“Ajax” 工具带:jQuery File Upload*

好的,那么除了使用 iframe 之外,没有其他方法可以上传文件。对吧?

因此,@jeferry_to 描述的使用 jQuery Iframe Transport 的 jQuery File Upload 是这项工作的工具。

*实际上工具/插件并没有改变任何东西..

现在呢?

嗯...我们需要访问传输 iframe 中的 S3 响应。但我们不能,因为它在不同的域上。 所以我们决定使用这个涉及第二个 iframe 的技巧来处理它。

设置:

TopFrame,我们的页面 (www.myhost.com) iframe TransportFrame (s3.amazonaws.com),由插件自动创建 - 包含 S3 响应 iframe XDMFrame (s3.amazonaws.com),在订购时访问 TransportFrame,获取响应并将其交付给 TopFrame

场景:

首先,我们需要修改 jQuery Iframe Transport,使其不会自动删除自动生成的表单和传输框架。我们需要这样做,因为稍后将使用的 #postMessage 本质上是异步的,我们不希望 iframe 在我们尝试访问它时消失。

    在 TopFrame 上,我们使用 jQuery File Upload 将文件上传到 S3。我们现在要确保它已上传,否则会出现错误。 TopFrame 使用#postMessage 向XDMFrame 发送跨域消息,提供TransportFrame 的名称。这条消息实际上是说:“嘿,检查 iframe X 的内容”以及何时将它们发回给我。 然后XDMFrame 执行top.frames['iframe X'].document.documentElement 之类的操作来访问TransportFrame 的内容,将它们字符串化并通过#postMessage 将它们发送回TopFrame。 TopFrame 接收消息,向用户显示正确的反馈,并删除由于我们的 jQuery Iframe Transport 修改而留下的表单和 iframe。

好的,现在一切都应该工作了,因为一切都是按书完成的。

不,你甚至不应该打扰。

您看...如果您强制现代浏览器使用 iframe 传输而不是 XHR2,上述解决方案确实会像魅力一样工作。

然而那是没有意义的。我们希望它在 IE8 + 9 中工作。

嗯...在 IE8/9 中它有时可以工作,有时不能。通常不会。

为什么?因为 IE 友好的 HTTP 错误信息。哦,是的,你读得很好。

如果发生错误,S3 会根据错误(400、403 等)响应 HTTP 错误状态。现在,根据here 所示响应的状态和长度,IE 会丢弃 S3 响应并用友好的错误消息替换它。为了克服这个问题,您必须确保响应始终 > 512 字节。在这种情况下,您无法保证任何类似的事情,因为您无法控制响应。 S3 确实如此,典型的错误小于 512 字节。

简而言之:

iframe 技巧适用于不需要它的浏览器,但不适用于需要它的浏览器。

不幸的是,我想不出其他任何事情,所以这个案子现在已经为我结案了。

【讨论】:

非常感谢您在这么长时间后仍抽空回复。发帖是个小技巧,谢谢分享。希望会有很多人发现它的用途:)【参考方案3】:

在 cmets 中总结我的答案: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

希望这会有所帮助!

【讨论】:

【参考方案4】:

对于“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 将永久设置为错误检查,以验证是否发生错误。

【讨论】:

S3 在出现错误的情况下接管框架,不幸的是,我得出了一个可悲的结论,即 IE 用户肯定只需要满足于“出现问题”而不是究竟是什么出错了 哦,我明白了...这很遗憾,但这意味着您知道它是否已被接管,只需向所述 iFrame 发送 postMessage,如果您收到回复,一切都很好,如果不是因为出错被接管了吗…… @JibiAbraham 我更新了脚本以跟踪 iframe 是否已被 S3 接管

以上是关于通过 iframe 上传 Amazon S3的主要内容,如果未能解决你的问题,请参考以下文章

通过标头进行 Amazon S3 对象生命周期管理

如何通过 PHP SDK 在 Amazon S3 存储桶下创建文件夹?

将流数据上传到 Amazon S3

基于浏览器的上传到 Amazon S3?

无法将应用程序上传到 Amazon S3

Amazon S3 上传文件并获取 URL