通过Iframe上传Amazon S3

时间:2013-01-29 08:27:50

标签: javascript iframe upload amazon-s3

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

我在互联网上见过勇敢的人谈论托管一个html文件,在要上传文件的同一个桶上,然后使用postMessages路由Iframe内容,或者那种东西。

有人可以向我解释如何实现这个神话般的解决方案吗? Blueimp的jQuery文件上传程序似乎解决了这个问题,但是上帝的代码是如此jQueryified,我还没有得到它的要点。

为清晰起见而编辑

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

4 个答案:

答案 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相关:

  1. 触发文件上传时,会调用send()函数。这个功能:

    • 创建隐藏的表单元素

    • 使用src="javascript:false;"创建iframe元素,并将load事件处理程序绑定到iframe

    • 将iframe附加到隐藏的表单,并将隐藏的表单附加到文档中。

  2. 创建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如果上传成功。

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

    • 提交隐藏表格

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

  3. 文件上传到S3。如果成功,Amazon会将iframe重定向到success_action_redirect网址。如果不成功,亚马逊将返回错误,该错误也会加载到iframe中。

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

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

    • 使用成功代码和对iframe的document对象(或undefined

    • 的引用来调用完整回调
    • 删除隐藏的表单(和iframe)

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

  6. 调用您的回调(传递给jQuery.ajax()success和/或complete)。插件始终返回成功代码,因此不会触发任何error回调。

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


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

答案 1 :(得分:3)

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

让我们了解事实:

  • 浏览器 IE8 / 9 //很少有机会成为没有FileReader支持的任何其他浏览器
  • 上传行为:出于用户体验原因,我们需要它为“Ajax”
  • 工具栏jQuery File Upload *

好的,没有其他方法可以上传文件而不是使用iframe。正确?

因此,使用jQuery Iframe Transport作为@jeferry_to的jQuery文件上传描述了这项工作的工具。

*实际上工具/插件不会改变一件事..

现在怎么办?

嗯......我们需要在传输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在我们尝试访问它时消失。

  1. 在TopFrame上,我们使用jQuery文件上传将文件上传到S3。我们现在想确保它已被上传,否则会收到错误。
  2. TopFrame使用#postMessage向XDMFrame发送跨域邮件,提供TransportFrame的名称。这条消息实际上说:“嘿,检查iframe X的内容”以及何时将em发回给我。
  3. 然后XDMFrame执行top.frames['iframe X'].document.documentElement之类的操作来访问TransportFrame的te内容,将它们字符串化并通过#postMessage将它们发送回TopFrame。
  4. TopFrame收到消息,向用户显示正确的反馈,并删除由于我们的jQuery iframe传输修改而遗留的表单和iframe。
  5. 好的,现在一切都应该有效,因为本书完成了一切。

    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检查,以验证是否发生了错误。