Flex's FileReference.save() can only be called in a user event handler -- how can I get around this?
Asked Answered
U

6

11

I need to call FileReference.save() after a web service call has completed, but this method has a restriction: "In Flash Player, you can only call this method successfully in response to a user event (for example, in an event handler for a mouse click or keypress event). Otherwise, calling this method results in Flash Player throwing an Error exception." (from the documentation here)

This restriction is a bit vague. Does it mean that I can only call the FileReference.save() method from within an event handler function that is registered as a listener for certain types of user events? If so then exactly which user events are valid? (Perhaps there's an event that will never be dispatched by user interaction with my application and I could register an event handler function for that event type and make the save() call from within that function?)

My difficulty is that I can't safely call the FileReference.save() method until my web service returns with the data that will be used as the argument of the FileReference.save() method call, so the event that triggers the FileReference.save() call is actually a ResultEvent rather than a user event, and I'm leery of dispatching a new (faux) user event type in order to be able to trigger the FileReference.save() call unless it's definitely a user event that would never be dispatched as a result of actual user interaction with my application.

In a nutshell what I'm doing now is this: I have a function that is registered as a handler for a button click. In this function I make my web service call to fetch data from the server. I also have a result handler function which gets invoked when the web service call completes, and it's in here that I want to call the FileReference.save() method since it's at this point that I know that the data is ready to be saved to a file. But the aforementioned restriction is blocking me from doing this -- I get an error:

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.

I've tried many things to get around this such as creating a second mouse click event handler function with the FileReference.save() call within and calling it after a timeout interval (to give the web service time to complete), but I keep running into the same error -- maybe that approach doesn't work since the second function isn't registered as an event listener for the event type used as its argument.

I'm new to Flex development so perhaps I'm just not thinking about this in the right way. If anyone can suggest another approach I'd really appreciate it. Thanks in advance for your comments or suggestions.

--James

Underpainting answered 21/7, 2010 at 17:0 Comment(0)
D
17

Adobe does this as a sort of security measure to ensure users are the ones messing with files rather than potentially harmful code. My understanding is that they enforce this by only allowing handlers of (click?) events that originate from UI components to execute the FileReference methods, so generating your own events programmatically will not work, although I have not tried to verify this. Unfortunately the best resolution I've found is to re-work the UI a bit to conform to this constraint. In your particular situation, you could make this a two click process with a button that says something like "Prepare Download", which changes to "Download File" after the web service is complete. This is less than ideal from a user perspective, but I don't think there's much else that can be done unless you can somehow complete your web service call prior to displaying the button that triggers the FileReference.save() call.

Dirk answered 21/7, 2010 at 17:42 Comment(1)
This is accurate. Browser based Flash applications are expected to obey the normal browser sandbox rules. Another option would be using Adobe AIR which gets outside of the browser sandbox (but requires the user to install the app).Costanzia
N
4

After struggling for that for well, a couple hours I found a workaround: you can use both mouseDown AND mouseUp events instead of just click.

For instance: s:Button mouseDown="prepare_PDF()" mouseUp="save_PDF()"

Works fine for me!

Happy coding!

--Thomas

Nothing answered 19/12, 2010 at 17:20 Comment(2)
Thanks for this, Thomas. I haven't messed with this application in some time so I may not ever get the chance to test this, but it looks like a good solution and is something good to know and keep in mind.Underpainting
If prepare_PDF is asynchronous, it seems like you'll be in a race-condition with save_PDF. What do you do in save_PDF if prepare_PDF hasn't completed yet?Bloodmobile
B
2

As a workaround I used the ExternalInterface class. I created a javascript function with this code

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

An in AS3 I call

var url = 'www.example.com/downloadfile.php?file_id=xxx';
ExternalInterface.call('downloadAttachmentFile', url);

So with that I transfer the file handling to JS/HTML.

Bria answered 12/11, 2012 at 18:11 Comment(3)
Please pay attention to whether you are replying to an active thread... this thread is 2 years old and has an accepted answer!Suburbanite
Yes but many people like me end up on "old" threads from google. So why not offer an alternative to another answer seeker like me?Bria
Agreed. I came here from a Google search, and want up-to-date answers, not just the one the OP liked!Coequal
E
0

This is a comment on Thomas' answer (I don't have enough XP to comment yet): The mousedown and mouseup workaround works nicely. Just a note that if you make any changes in prepare_PDF() that need 'undoing' in save_PDF(), then its a good idea to call that code on the mouseout event as well, since there might be a case that the user mousedown's on the button, but then moves the mouse away from the button.

This was particularly relevant for my case, in which we increase the size of a watermark on an image when the user clicks the download button (that triggers the .save() call). I reduce the size of the watermark down to normal on the mousedown and mouseout events.

Edina answered 9/2, 2011 at 12:28 Comment(4)
Since you're doing a web-service call, I don't see how this works. You fire off the webservice call from mouseDown and assume its been completed by the time mouseUp happens. What if mouseUp happens before the webservice call completes?Bloodmobile
@Bloodmobile the web-service call is being fired on mouseUp, not mouseDown.Edina
In Thomas' example, prepare_PDF (called from mouseDown) is the webservice call, right? Then save_PDF (called from mouseUp) calls FileReference.save.Bloodmobile
It's still confusing the way this is written, if we're doing an AMF RemoteObject call, how can we do the call after the save() is initiated in mouseDown()?Rafa
F
0

I had this same issue, I chose to use flash.net methods. Call flash.net.navigateToURL(url); from an actionscript or navigateToURL(url); from mxml.

Frederickfredericka answered 14/6, 2013 at 20:17 Comment(0)
P
0

What i do to solve this is to show an alert message with an anonymous function so i don't have to create a button.

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);
Piderit answered 19/12, 2016 at 18:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.