Friday, October 26, 2007

Flex and FileReference.Upload using Firefox and SSL

Either the SSL handshake doesn't complete because firefox creates a new session when using file protection sandboxing, or something more horrible is going on with the flash player - for whatever reason, file uploads in firefox over SSL don't work.

This post is a workaround until Adobe gets its act together and fixes this problem.
We will be using javascript and normal html forms to accomplish our upload.

First things first: Trap the case that the user is using firefox, and call a javascript function.

private function addFiles(s:String):void {
//if not IE:
if(flash.system.Capabilities.playerType != "ActiveX")
{
var imagename:String = "yourimagename";
flash.external.ExternalInterface.call("showUploadComponent",imagename);
}
else
{ //do normal image upload within flex
_refAddFiles = new FileReferenceList();
_typeUploadFile = s;

_refAddFiles.addEventListener(Event.SELECT, onSelectFile);
var imageTypes:FileFilter = new FileFilter("Images (*.jpg, *.jpeg, *.gif, *.png)", "*.jpg; *.jpeg; *.gif; *.png");
_refAddFiles.browse([imageTypes]);
}
}



Ok, now on the html file that hosts the swf, we need to make this javascript function available, and have it do the work.

<script language="JavaScript" type="text/javascript">
function showUploadComponent(imageName)
{
var w = window.open("","ARESUploadWindow","height=400, width=600,scrollbars=no,status=no,titlebar=no,toolbar=no");
var s = "<html><body><form method='POST' action='<?=$flexapi?>' ENCTYPE='multipart/form-data'><input type='hidden' name='request' value='imageUpload'/>Upload Image:<input type='file' name='"+imageName+"'/><br/><input type='submit' value='Upload'/></form></body></html>";
w.document.open();
w.document.write(s);
w.document.close();
}
function imageUploaded()
{
getFlexApp("<?=$swfname?>").updateImages();
}
function getFlexApp(appName) {
if (navigator.appName.indexOf ("Microsoft") !=-1) return window[appName];
else return document[appName];
}
</script>

Just after this script should be this part of the file that flex autogenerated:
<!-- BEGIN DeepLinking required section -->
<link rel="stylesheet" type="text/css" ...

Ok. So whats goign to happen here?
You perform an action within the flex app (clicking a button or something) which calls addFiles. In firefox, flex then calls the javascript function showUploadComponent. This method then creates a popup window, writes html to it to upload the image. Some of my code here will need to be changed for you - I'm using php and have some variable use in there to point it at the php file that accepts the upload. You will need to write this code yourself (it can be the same place fileReference.upload is posting to). The important thing is that this page after accepting the upload, then writes some html on success.
The way I do it in php is like so:

if($params['request']=="imageUpload")
{
if(UploadImages())
{
echo "<ok><script language='JavaScript' type='text/javascript'>window.close(); window.opener.imageUploaded();</script></ok>";
}
else
{
echo "<error type='imageUpload' description='Call to UploadImages() failed.'>";
print_r($results);
echo "</error>";
}

}


Basically, you'll want to print out that script that closes the window, and then calls the callback in window.opener. Lets revisit what that function does.
It calls getFlexApp and then calls a method updateImages inside the flex app. In my case, the user was uploading an image. I then wanted them to see their image inside the flex app after the upload completed. However, to accomplish javascript calling a flex method, you need to do something back in your flex app:
First you write the updateImages function (mine updates an image with id thumbImage):

public function updateDealImages():void {
this.thumbImage.source = "put where you saved the file to on the server here";
}

Then you need to register it as callable in your creationComplete function for the <Application creationComplete="onCreationComplete();">

public function onCreationComplete():void {
if (flash.external.ExternalInterface.available)
flash.external.ExternalInterface.addCallback("updateImages", updateImages);
}


Now you're good to go. This solution isn't out of the box ready, but should walk you through all the steps you need to write a javascript/html forms workaround for file upload in firefox under SSL.

2 comments:

Ash said...

My Flex code, I can't get FileReference.upload() to work in Firefox in http either.
It works fine in Internet Explorer. Is this a Flash Player bug. Is there a workaround to it without resorting to use HTML method of upload.

Scott said...

Have either of you figured this out?
We are trying to do some other things using Flash (no Flex) and SSL and they are failing in FireFox but not other browsers. scott@oceanmedia.net