Over a million developers have joined DZone.

How to Read a Local File with JavaScript and Flash

· Web Dev Zone

Start coding today to experience the powerful engine that drives data application’s development, brought to you in partnership with Qlik.

Here is the situation.  Your user has data on the client.  You want to get that data into your web application.  This results in uploading the data to the server first, then processing it there, and then sending it back.  Adobe Flash Player 9 introduced local file access, and you can use it from JavaScript with minimal impact to your existing application.  In this article you will learn the basics, and then see a slightly more robust example in action.

Reading a Text File

The first thing we need to do, is to give the user a place to click.  All file access operations must be initiated from a mouse click in the Flash Player.  This does not mean that your entire user interface (UI) needs to be delivered via the Flash Player.  In this case we will use a single button - the button that the user would otherwise click on to start the file access operation.

btnup = new Loader();
btnup.load(
new URLRequest(
this.loaderInfo.parameters["buttonup"]
)
);

addChild( btnup );

...

btnup.addEventListener( MouseEvent.CLICK, doClick );

...

protected function doClick( event:MouseEvent ):void
{
...
}

 

You will notice in this case that rather than use the Flash Player vectors to draw the artwork for the button, that I am loading the names of images from the HTML parameters to use for the various button states.  This lets you further control the appearance of the button without having to change or recompile the code.  In other words customization without having to know anything about developing for the Flash Player.

The class that does all the heavy lifting for us here is the FileReference class.  When the user clicks on the button, we will use the FileReference.browse() method to allow them to select a file.  To know when they have selected a file we will also want to listen for the Event.SELECT event.  Once this event has been received, we can call FileReference.load() to read the physical contents of the file.

file = new FileReference();
file.addEventListener( Event.SELECT, doFileSelect );
file.addEventListener( Event.COMPLETE, doFileLoaded );
file.browse();

...

protected function doFileSelect( event:Event ):void
{
file.load();
}

...

protected function doFileLoaded( event:Event ):void
{
var data:String = null;

data = file.data.readMultiByte(
file.data.bytesAvailable, UTF_8
);

ExternalInterface.call( "setData", data );

}

 

Keep in mind that the data file you load from the users system will need to be loaded into memory, so this might not be best for a multi-gigabyte movie file.  The Flash Player itself however, has been tested to files as large as 100 Mb which gives you a lot of breathing room.  You can get progress events as the file gets loaded, but in this case I am only going to worry about when the entire file read operation is complete.

That is it!  At this point, the FileReference.data property consists of a ByteArray object.  You can treat the bytes of the file as literal bytes and start parsing details of files such as EXIF information, or as textual content such as CSV or XML.  We will do the later for now, and also get that data over to JavaScript so it can be displayed in the user interface.

 

Exchanging Data with JavaScript

Content running in the Flash Player can call JavaScript methods by name, and pass arguments to them.  The class used to call JavaScript methods from the Flash Player is the ExternalInterface class, and there is not much to it.  For the purposes of calling JavaScript we merely need to call the static method ExternalInterface.call() and send the text data from the file. 

var setdata:String = loaderInfo.parameters["setdata"];
ExternalInterface.call( setdata, data );

...

// Using jQuery
function setData( data )
{
$( '#edit' ).attr( 'value', data );
}

 

The name of the JavaScript function to be used is specified as a parameter to the Flash Player content.  Again, this lets you reuse this example without having to change the Flash content.  When JavaScript gets called it then takes that text, and in this case, puts it into a large text area.  Now the user is free to edit the content, and they have never made a round trip to the server.

 

Saving a Text File

Remember earlier that I noted that load and save operations needed to be initiated by a user mouse click in the Flash Player.  If we want to save that changed content back into the file it came from, then we will need to let the user initiate that with a button presented in the Flash Player.  Again, the images for the button are loaded from HTML parameters to the Flash Player object.

When clicked the Flash Player calls the designated JavaScript function, it has merely to grab the text from the text area, and then return that value. 

When the FileReference.save() method is called, it takes a ByteArray object.  While JavaScript has no specific binary handling, there are times when you may want to handle binary data.  In these cases you will want to consider encoding the binary data with Base-64.  For now, we will simply create a new ByteArray object, and then write the text content into it using ByteArray.writeMultiBytes(). 

var data:String = ExternalInterface.call(
loaderInfo.parameters["getdata"] );
var bytes:ByteArray = new ByteArray();

bytes.writeMultiByte( data, "utf-8" );
file.save( bytes, "myfile.txt" );

 

With the ByteArray populated, we can pass it to the FileReference.save() method call.  The result will be a dialog box presented to the user.  At this point all the programming work is done.  The user on the other hand has a couple options.  While you can specify a default name, the user can change it if they prefer something else.  They also get to choose where to save the file.

 

Saving Image Files from Canvas

Among the exciting new features of the HTML5 specification, is support for CANVAS.  This blank slate opens the door to all fashion of different visualizations.  In the following example I let the user draw a picture using their mouse.  The question then becomes, how do you let the user save the picture to their local system?  Once again we find ourselves caught in the upload, process, send workflow.

As it turns out, canvas actually gives you access to the raw pixel data that has been drawn.  This might be user generated content, mathematically generated content, image content and more.  Since you now have the ability to save content without the round-trip to the server, you might be inclined to send an array of pixels over to the Flash Player, let it encode them to an image format, and then save the file out to disk.  And for the most part, you would be successful.

http://www.kevinhoyt.org/paint

 

Wait?  Encoding images?  Sure!  There are ActionScript libraries that do JPEG, PNG, BMP and more.  You pass in pixels, and get back the bytes of an image.  Sweet!

Well, not so fast!  Encoding an image takes time.  That is a lot of number crunching to be sure.  For example, a canvas that is a small 200 pixel by 200 pixels space is 40,000 pixels.  Each pixel consists of three values (red, green, and blue), which means 120,000 items to be reviewed at a bare minimum.  And 200x200 is not much creative space.  The larger the canvas, the more time it will take to encode that image.

While ExternalInterface is pretty snappy, passing an array of 120,000 integers will also slow us down.

It turns out that canvas has a toDataURL() method provided for by the specification.  Calling this method asks the browser, with its native code base, to make a PNG image file for us.  Since JavaScript does not have specific binary processing APIs, the result is a base-64 encoded string.  I have already showed you how to have Flash Player ask JavaScript for text, so you are almost there.

// Get the Base64 encoded PNG
// Inspired by http://bit.ly/930WOF
// Sending raw pixels is slow
// Let the native browser do the work

function getImageData()
{
return document.getElementById( 'stage' ).toDataURL();
}

...

var png:ByteArray = null;
var start:Number = 0;
var data:String = null;

data = ExternalInterface.call( "getImageData" );
start = data.indexOf( "," ) + 1;

png = Base64.decodeToByteArray(
data.substr( start, data.length - start )
);

file.save( png, "fromjs.png" );

 

Just as there are ActionScript libraries for encoding images, there are also ActionScript libraries for working with base-64.  In this case we can use one such library, the ActionScript Core Library (open source), to get the bytes back out of the encoded string by calling Base64.decodeToByteArray().  Once we have our ByteArray, we can call the FileReference.save() method.  At that point the user gets prompted to save the file.

It may sound a little more complex for this image approach, but it all happens in the blink of an eye, even with a larger canvas.

 

The Big Picture

Web standards are great, and they are growing in capability and sophistication.  Yet nowhere in the proposed specifications will you find local file access.  Sadly, you also will not find an API in JavaScript to support more direct binary manipulation.  Will these types of features eventually find their way into standards and browsers?  Probably!  But how long will that take?

Part of the reason we are seeing the web standards expand to include more functionality, is because users are demanding more compelling interfaces, and ultimately experiences.  These demands result in application requirements that were not originally envisioned with the previous specifications.  The interesting paradox that emerges is that it is much harder to differentiate yourself on user experience if everybody is using the same standards.

To that end, browsers have plug-in APIs for content such as Flash Player.  These plug-ins are not constrained by the standards process, and are free to innovate as fast as the market allows.  In the case of the Flash Player, that has been roughly one iteration every year, that is broadly distributed and nearly ubiquitous in about one year after that.  Does that mean you have to go all in on Flash?  No!

In these two examples we have used web standards to build the user interface and drive the core application logic.  We have extended those web standards with content running in the Flash Player to provide innovative new features such as local file access.  And we have even leveraged the native browser implementation to perform image encoding where we would have had problems scaling in both JavaScript and ActionScript.  The moral is simply, use the right tool for the job.

Download the source code for these examples to add local file access to your toolbox.

NOTE: The above ActionScript programs can be compiled using the freely available and open source, Flex SDK.  Should you desire a developer IDE, Flash Develop is a good open source option, Flash Builder is the commercial Adobe offering, and you can even use Visual Studio via the Amethyst plug-in from Sapphire Steel.  Or you know, if SWF provided here does what you need, then that works too.

Create data driven applications in Qlik’s free and easy to use coding environment, brought to you in partnership with Qlik.

Topics:

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}