Delivering Static Resources with Visualforce

Visualforce is a component-based user interface framework, typically used to display dynamic content. But not all application files and assets need to be dynamic. Some are unchanging, otherwise known as static. Force.com provides a facility to upload, manage and use these files, known as static resources. Examples of static resources include images, JavaScript files, Flash (SWF) files and cascading style sheet (CSS) files. This tutorial explains how static resources are created, referenced and cached on Force.com.

Managing Static Resources

Static resources are uploaded by the developer and managed by Force.com. From the Setup screen you can click Develop | Static Resources to see a list of all the resources that are currently stored, as well as upload a new one by clicking the New button. Each static resource also has a name, which uniquely identifies the resource. This is different from the actual filename of the uploaded resource. A static resource's filename may be called logo.gif but the resource's name may be set to company_logo. It's the latter that must be used to refer to this file from within Visualforce.

The following figure shows the typical data entered for each resource.
StaticResources Pic.png

Referencing Static Resources In Pages

To use a static resource within a Visualforce page, you don't simply hard-code a path to it. Instead, you use an expression. Expressions in Visualforce pages have the syntax of {! expression }. For the remainder of this article we'll omit the braces and exclamation point for brevity.

Static resources are referenced by creating an expression that evaluates to the URL you can use to refer to the static resource. The syntax is $Resource.<name> where <name> is the name given to the resource. This will evaluate to a relative URL of /resource/<timestamp>/<name> that Force.com understands as the URL for that resource's file. The <name> in the URL is the name of the resource and <timestamp> is the timestamp of when you last updated that resource's file. The timestamp is important to caching, as discussed later. So to reference an image resource with the name cloud, the code would look something like this:

<img src="{!$Resource.cloud}">

And this would evaluate to something like this:

<img src="/resource/1235794002000/cloud">

Similarly, a stylesheet could be referenced like this:

<apex:stylesheet value="{!$Resource.stylesheet_red}"/>

Which would evaluate to:

<link href="/resource/1235794002000/stylesheet_red" rel="stylesheet" type="text/css"/>

You would not typically care about the actual URL. What matters is simply the name of the resource - the expression language takes care of the rest for you.

Static Resource Bundles

There are often many static resources associated with an application. Rather than upload and name each one individually, Force.com lets you upload a ZIP file containing all the resources, and access individual files from within it. You can upload and name the ZIP file just as you would any other static resource.

To reference an individual resource from within the ZIP file, use the URLFOR function. The complete expression would look something like URLFOR($Resource.<name>,'<path>') where <name> is the resource name and <path> is the path to that file within the ZIP file. This path could simply be 'red.css' if the file is at the top level of the ZIP file, or it could be something like 'images/nav/header.jpg' if the file is within the images/nav folder within the ZIP. These will evaluate to the URLs /resource/<timestamp>/<name>/red.css and /resource/<timestamp>/<name>/images/nav/header.jpg respectively. Notice that the URL is the same as without a ZIP file, except it contains an extra path at the end. This is the correct path to the file within the ZIP.

In addition to this being a convenient way to upload a bunch of files, this also enables an important scenario: referencing a static resource from another static resource. Because static resources are inherently static, they cannot contain expressions, so how would a CSS resource reference an image resource? Don't ever be tempted to hardcode it. The answer is that they both need to be part of the same resource bundle (ZIP file). If the red.css file needed to reference the header.jpg file it could simply do so like this:

background-image: url(images/nav/header.jpg)

This works because the URL for the CSS file /resource/timestamp/stylesheet_red acts as the base URL and references to other files from within that CSS are treated as being relative from that location. So the above reference evaluates to /resource/timestamp/stylesheet_red/images/nav/header.jpg which is the correct URL of the header.jpg file.

Example using Static Resources

In the example below a static resource was created by creating a zip file of a directory called bootstrap. It shows how to reference assets that are in archive using the path to the file within the context of the archive.

Here is what the folder structure and archive structure looks like:


Archive.png

And here is the sample code that uses this archive.

<apex:page sidebar="false" showHeader="false" standardStyleSheets="false">
    <head>
        <meta charset="utf-8" />
        <title>Force.com Mobile Developer</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        
        <!-- Static Resources for CSS -->
        <link href="{!URLFOR($Resource.bootstrap, 'bootstrap/css/bootstrap.css')}" rel="stylesheet"/>
        <link href="{!URLFOR($Resource.bootstrap, 'bootstrap/css/bootstrap-responsive.css')}" rel="stylesheet"/>
        
        <!-- Static Resource for individual imag -->
        <link rel="apple-touch-icon-precomposed" sizes="144x144" 
    		href="{!URLFOR($Resource.bootstrap, 'img/apple-touch-icon-144-precomposed.png')}"/>
    </head>
    <body>
        <div class="navbar navbar-inverse navbar-fixed-top">
            <!-- snip -->
        </div>
    </body>
</apex:page>

Public Pages and Timestamps

As explained above, the generated URLs contain a timestamp. The reason this timestamp is incorporated is so that when you update a resource's file (for example by replacing a CSS file with a newer version) it will generate a different URL than before the update. This isn't so important when your Visualforce pages are part of an internal-only application, but this is critical when you are using Force.com Sites to expose your Visualforce pages publicly to the internet.

For public pages, Force.com uses a global content delivery network of cache servers that hold copies of your static files. If a URL request comes in for a file that is already in the cache, it serves up the cached version instead of retrieving it from the Force.com platform. This leads to a faster response time. Keep in mind that for this to work properly, you should set the Cache Control setting on the resource to Public.

So going back to the timestamped URL: if the URL didn't change when the file was updated, the caching servers would keep serving up the old file until the cache ran out (for approximately 24 hours). Because the timestamp of the last change is incorporated into the URL, when a file is updated a new URL is pushed out that the cache servers don't yet have, so they ask Force.com to serve up a fresh copy. Then that new URL and resource are cached until the next change. 


Sometimes developers are tempted to just hard-code the resulting URL (eg: /resource/1235794002000/stylesheet_red) into their Visualforce pages, but when you do this as opposed to using the $Resource.stylesheet_red expression, you are hard-coding the timestamp. In development you may not notice a problem, but in live production, if you ever updated a resource's file, the new version wouldn't start getting served up for around 24 hours or so. So if you were fixing a bug in a JavaScript file for example, you would have to wait a day for the file to become stale in the cache servers until a new copy was pushed out. In other words, never hard-code URLs!

Summary

Force.com has an elegant way of handling static resources, static material that can be referenced from Visualforce pages. You upload these resources to Force.com, then reference them in your Visualforce pages via an expression. You can upload individual files, or an entire package of files using a ZIP file. The latter approach also allows static resources to reference each other. Static resources are also cached - making use of the correct expression language to generate the URL for the static resource will ensure that this mechanism is utilized.

References