Flex的FileReference.save()只能在用户事件处理程序中调用--我该如何解决这个问题?

11
我需要在Web服务调用完成后调用FileReference.save()方法,但这个方法有一个限制:“在Flash Player中,您只能成功地调用此方法以响应用户事件(例如,在鼠标单击或按键事件的事件处理程序中)。否则,调用此方法会导致Flash Player引发错误异常。”(来自文档 这里
这个限制有点含糊。它是指我只能从已注册为某些类型用户事件侦听器的事件处理函数内部调用FileReference.save()方法吗?如果是这样的话,那么哪些用户事件是有效的? (也许有一种事件永远不会被应用程序的用户交互所调度,我可以为该事件类型注册一个事件处理函数,并从该函数内部进行save()调用吗?)
我的困难在于,在我的Web服务返回将用作FileReference.save()方法调用参数的数据之前,我无法安全地调用FileReference.save()方法,因此触发FileReference.save()调用的事件实际上是ResultEvent而不是用户事件,我很谨慎地想要分派新的(伪)用户事件类型,以便能够触发FileReference.save()调用,除非它绝对是应用程序用户交互的用户事件,否则我不会这样做。
简而言之,我现在正在做的事情是:我有一个函数,它被注册为按按钮时的处理程序。在此函数中,我进行Web服务调用以从服务器获取数据。我还有一个结果处理程序功能,当Web服务调用完成时会被调用,并且正是在这里,我想调用FileReference.save()方法,因为此时我知道该数据已准备好保存到文件中。但上述限制阻止了我这样做 - 我会收到错误:
Error #2176: Certain actions, such as those that display a pop-up window, 
may only be invoked upon user interaction, for example by a mouse click 
or button press.

我尝试了很多方法来解决这个问题,例如创建第二个鼠标点击事件处理程序函数,并在超时时间后调用它(以给Web服务完成的时间),但我仍然遇到相同的错误。也许这种方法行不通,因为第二个函数未注册为使用其参数的事件类型的事件侦听器。

我是Flex开发的新手,也许我没有以正确的方式考虑这个问题。如果有人能提出另一种方法,我会非常感激。预先感谢您的评论或建议。

--詹姆斯

6个回答

17

Adobe实行这种安全措施是为了确保用户处理文件而不是潜在的有害代码。我的理解是通过只允许来自UI组件的事件处理程序执行FileReference方法来实现这一点,因此通过编程方式生成自己的事件将不起作用,尽管我没有尝试验证这一点。不幸的是,我找到的最佳解决方案是对UI进行重新设计以符合这个限制。在您的特定情况下,您可以使用一个按钮来进行两次点击,按钮上写着“准备下载”,当Web服务完成后更改为“下载文件”。从用户角度来看,这并不是最理想的解决方案,但我认为除非您能够在显示触发FileReference.save()调用的按钮之前完成Web服务调用,否则没有其他办法可以做到。


2
这是准确的。基于浏览器的Flash应用程序应遵守正常的浏览器沙盒规则。另一个选择是使用Adobe AIR,它可以跳出浏览器沙盒(但需要用户安装应用程序)。 - James Ward

4

在挣扎了几个小时之后,我找到了一个解决方法:您可以使用mouseDown和mouseUp事件,而不仅仅是click。

例如: s:按钮 mouseDown =“prepare_PDF()” mouseUp =“save_PDF()”

对我来说很有效!

编程愉快!

- Thomas


谢谢你,Thomas。我已经有一段时间没有使用这个应用程序了,所以可能永远没有机会测试它,但它看起来是一个不错的解决方案,值得记住和牢记。 - James Adams
4
如果 prepare_PDF 是异步的,似乎您将会与 save_PDF 处于竞争状态。如果 prepare_PDF 尚未完成,那么在 save_PDF 中该怎么办? - paleozogt

2

作为解决方法,我使用了ExternalInterface类。我创建了一个JavaScript函数,代码如下:

function downloadFile (url) {
            window.open(url);
        }

在AS3中,我称之为
var url = 'www.example.com/downloadfile.php?file_id=xxx';
ExternalInterface.call('downloadAttachmentFile', url);

所以我将文件处理转移到JS/HTML中。

请注意您是否在回复一个活跃的线程...这个线程已经两年了,并且有一个被接受的答案! - durron597
8
是的,但像我这样的许多人最终会在谷歌上找到“旧”的帖子。那么为什么不为像我这样的另一个答案寻求者提供另一种选择呢? - havoc24k
同意。我是通过谷歌搜索来到这里的,想要最新的答案,而不仅仅是楼主喜欢的那个! - Venryx

0
这是对Thomas答案的评论(我还没有足够的XP来发表评论): mousedownmouseup的解决方法很好用。只是需要注意,如果在prepare_PDF()中进行了任何需要在save_PDF()中“撤消”的更改,则最好在mouseout事件上调用该代码,因为可能存在这样一种情况,即用户按下按钮,但然后将鼠标移开了按钮。
这对我的情况特别相关,在这种情况下,当用户单击下载按钮(触发.save()调用)时,我们会增加图像上水印的大小。我在mousedownmouseout事件上将水印的大小缩小到正常大小。

由于您正在进行Web服务调用,我不知道这如何工作。您从mouseDown触发了webservice调用,并假设在mouseUp发生时已经完成了该调用。如果webservice调用在mouseUp发生之前完成呢? - paleozogt
@paleozogt 网络服务调用在鼠标抬起时被触发,而不是在鼠标按下时。 - Jaffer
1
在Thomas的例子中,prepare_PDF(从mouseDown调用)是Web服务调用,对吗?然后save_PDF(从mouseUp调用)调用FileReference.save。 - paleozogt
这段代码的写法仍然令人困惑,如果我们正在进行AMF RemoteObject调用,那么在mouseDown()中启动save()之后,我们如何进行调用呢? - Mifune

0

我曾经遇到过这个问题,我选择使用flash.net方法。从actionscript中调用flash.net.navigateToURL(url); 或者从mxml中调用 navigateToURL(url);


0
我解决这个问题的方法是使用匿名函数显示警告消息,这样我就不必创建一个按钮。
Alert.show("Do you wish to download the file?", "Confirm", Alert.OK | Alert.CANCEL, this, function (eventObj:CloseEvent):void {
                                                                                                    if (eventObj.detail == Alert.OK) {
                                                                                                        fileReference.save(zipOut.byteArray, dateFormater_titulo.format(new Date ()) + ".zip");
                                                                                                    }//if
                                                                                                 }/*function*/, null, Alert.OK);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接