FileReference: the end of upload tyranny – guest post by Andrei Rosca
Posted by mzaharia on noiembrie 24, 2008
Filed Under Muncă, Tehnologie | 7 Comments
Today, we have a special guest on our blog: Andrei Roşca, who is a long time Flash enthusiast. Mainly a Flash developer, he is as passionate about Flex, AIR and Flash Lite.

Today, he is going to describe a solution/work-around to the long time problem of uploading files in Flash. So here it is:
With the arrival of Flash 10, FileReference gets some new goodies. A lot actually.
Now we can access data from the users without first uploading it. A win – win situation. Users don’t wait for the data to be available and we don’t have to worry (as much) about bandwidth and load times.
And better, we can save documents made on the fly without a server.
So, let’s start!
First we’ll open a image:
var imageFileRef:FileReference=new FileReference();
//supported image types
var imageTypes:FileFilter=new FileFilter("Images (*.jpg, *.jpeg, *.gif, *.png)", "*.jpg;*.jpeg;*.gif;*.png");
//the user selects a file
imageFileRef.addEventListener(Event.SELECT,loadImage);
//the file is loaded
imageFileRef.addEventListener(Event.COMPLETE,resterImage);
imageFileRef.browse([imageTypes]);
//start loading the image
function loadImage(e:Event=null):void
{
imageFileRef.load();
}
//from byte array to display object
function resterImage(e:Event=null):void
{
var loader:Loader=new Loader();
loader.loadBytes(imageFileRef.data);
addChild( loader);
}
That is the core, the very essence of my demo application. Use the FileReference to get the file – in my demo an image – in binary and find a way to read it.
While the code will work in the Flash IDE, the FileReference.browse() has security restrictions in browsers. You can call the browse method only in response to an user interaction: mouse click or key press.
What works: MouseEvent: CLICK, MOUSE_DOWN, MOUSE_UP and KeyboardEvent.KEY_DOWN, KEY_UP. Anything else will work in the Flash IDE but will throw an error when run in the browser.
In my demo, I have a browse button that triggers the file select pop-up window:
browseBtn.addEventListener(MouseEvent.CLICK, browseFiles);
function browseFiles(e:MouseEvent):void
{
imageFileRef.browse([imageTypes]);
}
Next I investigated the potential issues. It’s not uncommon to have large images 5-10MB. Unpacked in the Flash’s memory they have a devastating effect on the users computer.
My test image of 9MB unpacked as a 90MB in memory. Imagine what 5-10 images would cause.
Resizing the image is a straightforward process. We’ll use the BitmapData.draw();
//from byte array to display object
function resterImage(e:Event=null):void
{
var loader:Loader=new Loader();
//
loader.contentLoaderInfo.addEventListener(Event.COMPLETE,resizeImage);
loader.loadBytes(imageFileRef.data);
}
//make the image, small, very small..
function resizeImage(event:Event):void
{
var info:LoaderInfo=LoaderInfo(event.target);
var loader:Loader=Loader(info.loader);
//make the image 50 px in height
var scale:Number=50/loader.height;
//and actually resize it
loader.width*=scale;
loader.height*=scale;
loader.width=Math.round(loader.width);
loader.height=Math.round(loader.height);
//use a container so we don't need a matrix in BitmapData.draw
var container:Sprite=new Sprite();
container.addChild(loader);
//take the snapshot
var bmpData:BitmapData=new BitmapData(container.width,container.height);
bmpData.draw(container);
var bmp:Bitmap=new Bitmap(bmpData);
addChild(box);
}
While we end up with a small image, the memory requirements are the same.
The problem is that the FileReference holds the original huge image in the data property. It’s a read only so we can’t delete it. The documentation does not mention any way do unload the file.
After some serious doubts I have found a way:
imageFileRef.cancel();
While not documented, the cancel also clears the data byte array.
There. It’s done. You can use a thumbnail of the image, you have a FileReference if you plan to upload the image in the next step and the memory usage is under control.
What more can we do?… Well, we could create images too!
FileReference.save() makes this extremely simple. No more servers involved. No more upload and then download! Bliss!
Be aware that save() has the same security restrictions as browse(). The user has to interact with your application.
//the area where our users will play
var canvas:Sprite=new Sprite();
function saveFile(e:MouseEvent):void
{
var saveFileRef:FileReference=new FileReference();
//take a snapshot of the canvas
var bmpData:BitmapData=new BitmapData(stage.width,stage.height);
bmpData.draw(canvas);
//use the encoder from Flex - or other of your choice
var pngEncoder:PNGEncoder=new PNGEncoder();
//ask the user to save the file
saveFileRef.save(pngEncoder.encode(bmpData),'canvas.png');
}
Isn’t it beautiful?
Comments
7 Responses to “FileReference: the end of upload tyranny – guest post by Andrei Rosca”
Leave a Reply


I’ve accidentally came across this article, haven’t got time to read it, but I’ve notices something: the code looks… funny:) I recommend this tool, I use it to transform code to HTML:
http://www.interfete-web-club.com/demo/pretty/Prettify.html.
The sample crashes in IE. MemoryUsage increeses with each pictures and never gets cleared. Somehow it works fine in Firefox.
Way to go, Andrei!
is there now a easy way to upload the image to a webserver with FileReference.upload()? I’m trying to port a app I created in Air, which should be quite simple with this new Flash player 10 stuff, but seems I come a cropper when trying to upload.. Although the upload does say it’s completed and even the progress event traces the kb’s uploaded… nothing is in the folder that it’s supposed to get uploaded to! Here’s my upload actionscript code..
file.upload(new URLRequest(UPLOAD_URL + “/index.php”), “FileDaty”, false);
file.addEventListener(Event.COMPLETE, uploadCompleteHandler);
file.addEventListener(ProgressEvent.PROGRESS, progressHandler);
Thank you for your post! We tried implementing the FileReference this way, but we face some serious memory issues when selecting a lot of images at the same time. Calling fileReference.cancel() does not seem to clear the data ByteArray. We found out the memory is only cleared when there is no reference to the FileReference instance anymore and the Garbage Collector removes it.
It seems like Adobe only created this feature for loading small files. I prefer the solution provided by Silverlight. It lets you access the data through a Stream, so not all data will be loaded into memory at once.
Good job man!
[...] http://myadobe.ro/2008/11/24/filereference-the-end-of-upload-tyranny-guest-post-by-andrei-rosca/给出了解决方案。大致的思想是,在Loader加载到图片数据后,做如下处理: [...]