As mentioned, in this article you will learn how to use Flash to add extra capabilities to your Web applications. Familiarity with JavaScript is the most necessary skill, but experience with ActionScript will be handy. There are a variety of ways to compile ActionScript, some of which depend upon Adobe's commercial tools. You can, however, just use the open source Flex SDK from Adobe. For this article, Flex SDK 4.0.0.10485 (Beta 2) was used. To run the examples, you will need the Flash Player, version 10 or higher. I use version 10.0.42.34 throughout this article. See Resources for links to acquire these downloads.
Many Web applications need to save state on the client. Sometimes this may just be a session ID of some sort that can be used to retrieve server-side state from memory or a database. However, many Web applications deliberately avoid server-side state for the sake of scalability. So any state must be stored on the client. In addition, it is often desirable for this state to persist beyond the user's current session.
HTTP cookies have been the default way to handle this for years. However, HTTP cookies have their drawbacks. They can be difficult to use from a developer perspective because they are physically just an HTTP header. More importantly, they are a potential security liability. HTTP cookies are sent back and forth between the client and server on every request, so any data in them could be intercepted. Further, cross-site scripting/forgery often takes advantage of this fact to "steal cookies." If you get your cookies stolen, your corresponding account can be easily compromised and even taken over.
But, the most common drawback of HTTP cookies is the size limitation. Different browsers place different limits on the maximum size of HTTP cookies allocated per domain. The HTTP specification places the limit at 4 KB, and this is all you can count on. So what to do if you need to store more than 4 KB on the client? If you have ever thought it would be fun to write a gzip-style compression algorithm in Java™Script, then this is your opportunity. Or you can use an alternative, like Flash.
The Flash Player gives Flash applications local storage space. By default, this has a limit of 100 KB per domain. That is right: you get twenty-five times as much space as you would with HTTP cookies. There are also some other key differences. One, client-side data in Flash is never sent to the server, as it has nothing to do with HTTP. Of course, you can take this data and send it to your server if you want to. There is obviously nothing stopping you from doing this. However, you have to choose what data to send and how it is sent. If you really need this data on both the client and server, this can actually become a bit tedious. Although, this is generally much more secure, because you must explicitly "expose" this data over the wire.
The Flash API for storing and retrieving local data is known as a
SharedObject. Flash actually has a notion of SharedObjects that can be
remote as well, hence the client-only variety is often known as a local
SharedObject. The API is very simple, and
it allows for the storage and retrieval of arbitrarily complex objects using a
key-value paradigm. Listing 1 shows simple code for storing and retrieving SharedObjects.
Listing 1. Store and retrieve
SharedObjects
package{
import flash.display.Sprite;
import flash.net.SharedObject;
public class JsHelper extends Sprite{
private const SO_NAME:String = "helperSo";
private function saveLocal(name:String, value:Object):void{
var so:SharedObject = SharedObject.getLocal(SO_NAME);
so.data[name] = value;
so.flush();
}
private function readLocal(name:String):Object{
var so:SharedObject = SharedObject.getLocal(SO_NAME);
return so.data[name];
}
}
}
|
If you are unfamiliar with ActionScript, the code in Listing
1 might look a little odd to you. ActionScript is very similar to
JavaScript; in fact, it is derived from the ECMAScript standard. However,
it has many features usually seen in languages like C++ and Java.
Variables are strongly typed, and there is class-based inheritance. The
code in Listing 1 is a class that extends the
Sprite class. Such a class will compile into a
Shockwave Flash (SWF) file that can be embedded in a Web page. This class has two methods. One is
called saveLocal. This takes a name (which is
just a string) and a value (which can be any type of object). It then gets
a particular SharedObject using the
getLocal factory method.
Each SharedObject instance has a data property
that can be thought of as a hashtable for storing data. That is what the
second line of the saveLocal function does. The
last line "flushes" the SharedObject, or
persists it to disk. That is all you are required to do to save locally.
If you make heavy use of SharedObjects and
start to get close to the 100 KB limit, then you may want to attach event
listeners to it. This will let you respond to events like "flush
completed" or "flush failed."
Reading back from local storage is just as straightforward and is done in
the readLocal function in Listing
1. In this case, the SharedObject is
looked up, and the name parameter is used to
look up the saved object from
the data hashtable. If name does not correspond to a key in the hashtable,
then null will simply be returned. Now that you have seen how to access
SharedObjects, you just need to get this Flash
(invisibly) on a Web page -- and access it from JavaScript.
By default, none of the functions shown in Listing 1 will
be accessible outside of the JsHelper class. However, Flash
makes it very easy to expose functions to JavaScript. All you have to do
is use Flash's ExternalInterface API, as shown
in Listing 2.
Listing 2. Exposing
JsHelper functions to JavaScript
package{
import flash.external.ExternalInterface;
import flash.net.SharedObject;
public class JsHelper extends Sprite{
private const SO_NAME:String = "helperSo";
private const SAVE_LOCAL:String = "saveLocal";
private const READ_LOCAL:String = "readLocal";
public function JsHelper(){
ExternalInterface.addCallback(SAVE_LOCAL, saveLocal);
ExternalInterface.addCallback(READ_LOCAL, readLocal);
}
// functions omitted for brevity
}
}
|
Listing 2 simply shows what was added to the
JsHelper class from Listing 1.
The main thing to notice is that you have added an explicit constructor.
This will be called whenever an instance of this class is created, just as
you would expect. In it, you use the
ExternalInterface API to expose the two
functions, saveLocal and
readLocal, to any JavaScript on any Web page
where this SWF will be embedded. The
addCallback function takes a string as its
first parameter. This will be the name used by JavaScript clients to
identify the function. It could be different than the function name in the
class, but in this case they are the same. The second parameter is a
function closure. Like JavaScript, ActionScript is a functional language,
so functions are first-class and can be passed around. This is all that is
needed to expose the two functions. Now take a look at the JavaScript for
embedding and accessing the SWF. This is shown in Listing 3.
Listing 3. Embedding invisible Flash
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Flash Helper for JavaScript</title>
<script type="text/javascript" src="swfobject.js"></script>
<script type="text/javascript">
function writeFlash(){
var attrs = {id : "JsHelper"};
swfobject.embedSWF("JsHelper.swf", "flashContainer", "1", "1", "10.0.0",
"playerProductInstall.swf", null, null, attrs);
}
function save(name, value){
var helper = document.getElementById("JsHelper");
helper.saveLocal(name, value);
}
function load(name){
var helper = document.getElementById("JsHelper");
return helper.readLocal(name);
}
</script>
</head>
<body onload="writeFlash()">
<div id="flashContainer"></div>
</body>
</html>
|
The first thing to notice about this code is that it uses a third-party
JavaScript library called swfobject. This is
the de facto standard library for embedding SWFs. It is open source, and
while it was not originally developed by Adobe, it is distributed by them
now. Its embedSWF function is used to embed the
SWF compiled from Listing 2. The first parameter is the
URL to the SWF. In this case, the SWF is being served from the same server
and path as the HTML file, so a relative URL works fine. The second
parameter is the ID of the HTML element on the page where the SWF will be
embedded within. In this case, it is "flashContainer"—and you will
notice that in the body of the HTML document there is an HTML div with
this ID.
The next two parameters to embedSWF are the
height and width of the SWF. In this case, they are both 1. That means
that your SWF will be one pixel tall and one pixel wide. This is the
invisibility of SWF! The next parameter is the minimum Flash version that
your JavaScript will check for. If Flash is installed on the browser, but
it is an earlier version than the minimum version you passed to
embedSWF, then
embedSWF will use the next parameter
"playerProductInstall.swf". This is the URL to
another SWF used to prompt the user to install the latest version of
Flash. For invisible Flash, this does not really matter—the "you need
to install the latest version of Flash" SWF is also going to be invisible
(well, a 1x1 pixel anyway). The last parameter passed to embedSWF is important.
This is an attributes object that can contain various optional
parameters. One of those optional parameters is an ID for your SWF. For a
use case like the one in this article, this parameter is not
optional—it is critical! This will give your SWF an HTML ID, which you will need to
access it programmatically using JavaScript.
Now take a look at the two JavaScript functions in Listing
3. They are very similar. Both use the familiar getElementById
function to get a reference to the SWF. Notice they use the ID specified
in the attrs object in the writeFlash function. After you get a reference
to the SWF, you can directly call the ActionScript functions exposed in Listing 2. The key here is that the function name used
in JavaScript must match up to the name exposed through the
ExternalInterface.addCallback function or the
first parameter passed to addCallback.
For the save method, you are passing a JavaScript object in as the value parameter. This can be any object, even one with a deep nested structure. However, only its data will be passed in. If the object has any functions, those will not be passed. Notice that the load method returns whatever comes back from the SWF. What will this be? The answer is simple: whatever you sent. If you stored a scalar value like a number or string, then that is what will come back. If you stored a complex object, then that JavaScript object is what will come back—no need to parse, and so on. One exception is that if your object has functions, those can obviously not be serialized and saved. So it is just the data from your object, no more, no less. That concludes everything you need to know about using Flash for local storage. Before moving on and looking at cross-domain Ajax with Flash, examine some of the alternatives for local storage that may be available to you.
I mentioned that Flash can be an attractive option to HTTP cookies for
client-side storage. However, there are other Web technologies that have
adopted the same paradigm as Flash's SharedObjects. In fact, for years the
various browsers have provided very similar APIs. However, these APIs were
usually different from one browser to the next. So you could do some
browser sniffing and then use the browser-specific API. Flash provided a
consistent alternative. The code developed in this article will work on
virtually any browser: Internet Explorer 6 and Firefox 2, all the way up to the latest
versions of those browsers. The newer versions do provide a consistent
alternative. The HTML 5 specification includes a
localStorage API. This is implemented in the
latest version of the major browsers, including IE 8 and Firefox 3.5. So
if you only have to worry about those browsers, then localStorage is a
viable option. If you have to worry about older browsers (IE 6, IE 7, and
so on), then it may be simpler to stick with Flash. Now, take a look at
another place where Flash can open up new capabilities, cross-domain
Ajax.
Ajax has become ubiquitous for Web applications; it is an assumed part of
any Web application. One of the major constraints of Ajax is the notorious
same origin policy. If your Web page was served from a.com, then you can
only make Ajax (or more precisely,
XMLHttpRequest) calls to a.com. You cannot call
b.com, for example. It does not matter if your company owns both a.com and
b.com; the browser does not care. However, this is not the case for Flash
applications.
Flash applications can be granted permission to make calls to domains other than the one that they were served from. This is done using a cross-domain policy file. By default, the Flash run time will look for this policy file at the document root of your server: http://<your domain>/crossdomain.xml. For example, Listing 4 is the policy file for Twitter's search domain, http://search.twitter.com/crossdomain.xml.
Listing 4. Policy file for Twitter search domain
<!DOCTYPE cross-domain-policy
SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<allow-access-from domain="*" />
</cross-domain-policy>
|
This is an especially nice policy file. It is granting access to SWFs from any (as indicated by the "*" wildcard) domain. So any Flash application can call Twitter's search APIs. Policy files like this are common from major Web sites with public APIs. However, some sites also use more restrictive policies that only allow access from other domains that they own or have partnerships with. With policy files, you get fine-grained control over exactly who can call your servers and who cannot. Take a look at how you can extend these same abilities to Ajax applications.
If your application only needed to call a specific domain, you could write
ActionScript code that called that domain, and then expose it using
ExternalInterface to JavaScript in your
application. However, I will take a little more ambitious route and try
to expose this generically. Thus, in Listing 5 you will
see a utility class for calling any domain from ActionScript.
Listing 5. Cross-domain utility SWF
package{
import flash.display.Sprite;
import flash.events.Event;
import flash.external.ExternalInterface;
import flash.net.URLLoader;
import flash.net.URLRequest;
public class JsHelper extends Sprite{
private const SEND_REQUEST:String = "sendRequest";
public function JsHelper(){
ExternalInterface.addCallback(SEND_REQUEST, sendRequest);
}
public function sendRequest(url:String, handlerName:String,
method:String="GET", content:Object=null,
headers:Object=null):void{
var loader:URLLoader = new URLLoader();
var request:URLRequest = new URLRequest(url);
if (method){
request.method = method;
}
if (headers){
for each (var name:String in headers){
request.requestHeaders[name] = headers[name];
}
}
if (content){
request.data = content;
}
loader.addEventListener(Event.COMPLETE,
function(e:Event):void{
ExternalInterface.call(handlerName, loader.data);
});
loader.load(request);
}
}
}
|
This class exposes its sendRequest function to
JavaScript using ExternalInterface. This is done
in the object's constructor, just as you've done in the previous example
in Listing 2. The sendRequest
function is a little more complicated: It has two required parameters. The
first paramater is the URL that you want to call. This is the full URL string,
including any request parameters. Next is the name of the JavaScript
function that you want Flash to call when it gets a response from the
server. As with typical Ajax, Flash makes asynchronous calls to remote
servers so that the main UI thread is not frozen while awaiting a
response from the remote server. Therefore, just like with Ajax, you need to write
a callback function that will get invoked after
a response is received from the server. You pass this into Flash as a string, but it must be the
exact name of the function.
The sendRequest function has three optional
parameters as well. ActionScript lets you have optional parameters,
but they must have default values to use. The first of these is the HTTP
method to be used, which is typically either
GET or POST. In this
article, I
default to GET, but you can easily override it
to POST if that is required by the remote
server. The next optional parameter is called
content. This is a generic data object that
would contain any name-value pairs that you want to send to the remote
server. You would need to use this if you are posting data to the remote
server. Finally, the last optional parameter is another generic data object
for headers. This is for setting custom HTTP headers to send as part of
the call to the remote server.
The code then takes all of these parameters and constructs an HTTP request
using Flash's URLRequest object and sends the
request using Flash's URLLoader class. An event
listener is wired up to the URLLoader so that
you can process the response after it is returned. Here you create a closure,
just like you would do in JavaScript, to create an anonymous inline
function that will be invoked when the COMPLETE
event is fired by the loader. This closure will simply use
ExternalInterface to call the function whose name
was passed in to sendRequest. It will pass all
of the data from the remote server to that function. This is definitely a
little more of a complex use case than the local storage one. Take a look
at Listing 6 for an example that calls Twitter search.
Listing 6. Calling Twitter search
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Calling Twitter from the client</title>
<script type="text/javascript" src="swfobject.js"></script>
<script type="text/javascript">
function writeFlash(){
var attrs = {id : "JsHelper"};
swfobject.embedSWF("JsHelper.swf", "flashContainer", "1", "1", "10.0.0",
"playerProductInstall.swf", null, null, attrs);
}
function search(){
var keyword = document.getElementById("keyword").value;
var helper = document.getElementById("JsHelper");
helper.sendRequest("http://search.twitter.com/search.json?q=" + keyword,
"showResults");
}
function showResults(responseStr){
var response = eval("(" + responseStr +")");
var results = response.results;
alert("#Results = " + results.length);
}
</script>
</head>
<body onload="writeFlash()">
<div id="flashContainer"></div>
<div id="inputDiv">
<label for="id">Search Twitter</label>
<input type="text" id="keyword" name="keyword"/>
<input type="button" value="Search Twitter" onclick="search()"/>
</div>
</body>
</html>
|
The code here is similar to the HTML/JS you saw in Listing
3. Again, you use the swfobject
library to embed the SWF on the page. This code has a simple form that
prompts the user to enter a keyword to search on Twitter. The search
function is invoked after the Search button is clicked. This pulls the
keyword from the input field and uses it to create the URL string that
you want to call. This is passed to the JsHelper SWF, just like you did in
Listing 3, by getting a reference to the SWF using its ID
and then directly calling the function exposed using ExternalInterface. You
pass in the URL and a string naming the callback function. After the data
is returned from Twitter, it is passed to the
showResults function. This will be a JSON
string because that is what comes back from Twitter, so you can simply use
JavaScript's eval function to turn it into an
object. In this case, you simply show the number of results returned from
Twitter. However, you could easily create some HTML on the page that
listed each of these results; it's just a matter of boilerplate DOM code.
Just like that, you have made a cross-domain Ajax call courtesy of
Flash. As with using Flash for local storage, there are some other
options that you should be aware of.
For years, Flash has made it possible to do cross-domain calls, just like it has made it possible to do local storage for years. Similar to the local storage use case, some Web browsers have come up with their own way to do this, and there has been subsequent attempts at standardization. It has not been as nice of a story as it was with local storage. Even though there is currently a standard for cross-domain Ajax, it is not adopted by IE 8. Instead, IE 8 has a proprietary way of doing cross-domain Ajax. This proprietary implementation is new in IE 8 and not present in older versions of IE. So once again, Flash presents a model that works on all browsers.
Finally, if you do any research into cross-domain Ajax, you will surely find other ways to do it that do not require Flash or any new features in browsers. This technique, usually referred to as JSONP (JSON with Padding) or "dynamic script tag," takes advantage of the fact that the browser does not enforce the same origin policy on JavaScript source files. So a script tag is created whose source points to the URL that you want to call, and is inserted into the DOM. The URL usually includes a "callback" parameter, which is the name of the callback function that you want invoked. The server then wraps the data with the function name so that the function gets invoked with the data from the server passed into it. This technique is widely used on the Internet (for example, the Twitter search API showed earlier supports this technique), but it has serious security risks associated with it—therefore, the desire for a standard, safe way to do this, similar to how Flash does it.
This article explained how to use Flash to expand the capabilities of
your Web applications. You can dramatically increase how much
local storage is available to your application. This allows you to cache
large amounts of data on the client. You can also make cross-domain Ajax
calls from your Web applications using Flash. This is only a small taste
of some of the capabilities of Flash. If you find something else that will
help you, you can use the same techniques: make the Flash invisible and
expose its functions using ExternalInterface.
| Description | Name | Size | Download method |
|---|---|---|---|
| Article source code | JsHelper.zip | 12KB | HTTP |
Information about download methods
Learn
-
"Lights, camera, ActionScript 3.0!" (developerWorks, April 2007): Get a nice introduction to ActionScript.
-
"Using Flex SDK with Mate and PHP" (developerWorks,
July 2009): Learn about using the Flex SDK.
- The Flex SDK is part of the Flex
framework, a popular, Flash-based framework for creating Rich Internet
Applications. Explore its capabilities in the developerWorks article "Rich Internet Applications with Grails, Part 1: Build a Web
application using Grails and Flex" (developerWorks, February 2009).
-
"Cross-domain communications with JSONP" (developerWorks, February 2009): Learn about using JSONP for
cross-domain Ajax.
-
Flex
Wiki: Get the latest news on the Flex SDK and the latest version
of the SDK.
-
DOM Storage:
Read about HTML 5's localStorage support in Firefox by reading this
Mozilla article.
-
XDomainRequest: Read documentation on IE 8's cross-domain
implementation.
-
HTTP access
control: Find out how Firefox does cross-domain Ajax by reading
the Mozilla article.
- The developerWorks Web Development zone
specializes in articles covering various Web-based solutions.
-
My
developerWorks: Personalize your developerWorks experience.
- To listen to interesting interviews and
discussions for software developers, check out developerWorks podcasts.
-
developerWorks technical events and Webcasts: Stay current with
developerWorks technical events and Webcasts.
Get products and technologies
- Download the Flex SDK.
- Innovate your
next open source development project with IBM trial
software, available for download or on DVD.
Discuss
- Participate in developerWorks
blogs and get involved in the developerWorks community.

Michael Galpin is an architect at eBay. He is a frequent contributor to developerWorks. He has spoken at various technical conferences, including JavaOne, EclipseCon, and AjaxWorld. To get a preview of what he is working on next, follow @michaelg on Twitter.




