JavaScript filesystem access

From Trephine

Jump to: navigation, search
« Self-replicating JavaScript Objects Why trephine is better for applet development »

[subscribe] Recent blog entries

Live Demos

JavaScript filesystem access

Using only native browser capabilities, it is impossible for a web application to access the user's hard drive. However, trephine allows users to opt-in and grant more privileges to scripts on a page. This article explains how to interrogate the client's filesystem using JavaScript and trephine.

Tip: To see this in action, check out the file browser demo.

The dirInfo function below takes a path string and returns a JavaScript object containing information about that directory. This object has three fields (properties):

  • path - the corrected Operating System dependent path
  • subdirs - sorted list of subdirectories
  • files - sorted list of files in this directory

If the path passed in is not a directory or is not readable by trephine, dirInfo() will throw an exception explaining the problem.

Here's the completed function:

function dirInfo( path ) {
  var result = trephine.js( function( path ) {
    var dir = new java.io.File(path);
    if (!dir.isDirectory()) throw "Path is not a directory: " + path;
    if (!dir.canRead()) throw "Path is not readable: " + path;
    var list = dir.listFiles(), files = [], subdirs = [];
    for (var i=0, l=list.length; i<l; i++) {
      var name = list[i].getName() + '';
      if (list[i].isDirectory()) subdirs[subdirs.length] = name;
      else files[files.length] = name;
    }
    subdirs.sort();
    files.sort();
    return JSON.stringify( {
      path: dir + '', subdirs: subdirs, files: files
    } );
  }, path );
  if (result.error) throw 'Error: ' + result.error;
  return eval( '(' + result.result.toString() + ')' );
}

To understand how this works, let's look at a piece at a time, starting with the interface between browser JS and trephine:

  var result = trephine.js( function( path ) {
    // ... code executed in the trephine Rhino context
  }, path );

This code invokes the trephine.js function, passing in an anonymous function to execute in the Java/Rhino context. It also passes forward the path variable which had been sent into dirInfo(). The result of the trephine invocation is stored in a variable called result which is an object containing the following member properties:

  • success - a boolean value flagging the success of the operation (false if an exception was thrown, true otherwise)
  • result - the actual return value of the operation (may be null or undefined)
  • error - if an exception was thrown, the error property will contain a string representation of it.

At the bottom of the dirInfo function, we see how this result object is used:

  if (result.error) throw 'Error: ' + result.error;
  return eval( '(' + result.result.toString() + ')' );

If any exception is thrown in the Java/Rhino context, the first line above will rethrow it as a JavaScript exception. The second line uses eval() to deserialize the output, which is JSON (as we'll see in a bit).

The internals of the anonymous function are where things get interesting:

    var dir = new java.io.File(path);
    if (!dir.isDirectory()) throw "Path is not a directory: " + path;
    if (!dir.canRead()) throw "Path is not readable: " + path;

This code simply converts the path string into a java.io.File object called dir. Then, if it turns out that dir is not actually a directory, or the process doesn't have sufficient rights to read it, we fail fast by throwing an exception. Recall that this execption will be rethrown in the browser JS environment, so it may make sense to wrap calls to dirInfo in a try/catch block.

After the checks are taken care of, the code iterates over all child objects in the directory and builds a list of subdirectories and a list of files:

    var list = dir.listFiles(), files = [], subdirs = [];
    for (var i=0, l=list.length; i<l; i++) {
      var name = list[i].getName() + '';
      if (list[i].isDirectory()) subdirs[subdirs.length] = name;
      else files[files.length] = name;
    }

Finally, the two lists are sorted and set as properties on the JSON serialized return object (along with the corrected, OS-dependent path to the original dir):

    subdirs.sort();
    files.sort();
    return JSON.stringify( {
      path: dir + '', subdirs: subdirs, files: files
    } );

The step of sorting the subdirectories and files before returning them is merely a convenience, and could be omitted in a more performance-conscious implementation.

In order to use this code, you'll have to hotlink the trephine.js file and invoke trephine.load() as explained on the getting started page and demonstrated in any of the interactive demos.

That's about all there is to it. If you have any questions, please leave a comment!

Public domain declaration

Just so there's no confusion: all of the code snippets on this page are provided "AS IS", without warranty of any kind, express or implied.

All of the code snippets on this page are hereby released into the public domain by the me, the copyright holder. This applies worldwide. Or in case this is not legally possible: The copyright holder grants any entity the right to use this work for any purpose, without any conditions, unless such conditions are required by law.

If you'd feel better with a "real" license, you're free to use code snippets on this page under the MIT license as described on the about page.

Any links back to this site are always appreciated, but not required. Enjoy!

--Jim R. Wilson (jimbojw) 18:22, 10 April 2009 (UTC)
Personal tools