File I/O API for widgets
By Opera Software · 15 May, 2008
Note that you can find links to the API documentation, example downloads etc, here.
The basics
The File I/O API consists of three classes: FileSystem, File and FileStream.
The FileSystem class is initialized as a singleton, and is available as opera.io.filesystem. This is a virtual file system. In order to actually use it, you'll need to add directories from your actual file system as mount points to the virtual filesystem.
The File object works like similar objects in other frameworks. It can point to a directory, archive or regular file. It exposes properties like path, isDirectory, exists, parent, etc. It also works as an array to let you access files and subdirectories in a directory.
The FileStream is used when you want to read from or write to the files in the filesystem. It supports reading and writing text, images, binary data and Base64 text.
Enabling File I/O
In order to make the file system and its methods available, you need to add a file attribute with the value 'yes' to the widget element in the config.xml file of your widget.
Mount points
Instead of accessing the file system directly, this API uses a concept of mount points. In order to access parts of a disk, it must first be mounted as a mount point in a virtual filesystem. You create mount points by calling opera.io.filesystem.browseForDirectory() or browseForFile().
These functions will open a file dialog, and let the user choose a file to share. The selected file is returned as an argument to a callback function. If the user cancels the dialog, or the selected file is somehow invalid, the callback function is called with null.
The following is an example using browseForDirectory(), which is the most common case:
opera.io.filesystem.browseForDirectory( 'share', '', processDir );
//Let the user choose a directory
function processDir( dir )
{
if ( ! dir )
{
return; //Invalid file or canceled dialog
}
opera.postError(dir.path);
}
In this case, 'share'; becomes the name of the directory in the virtual file system.
The processDir function is called with the file the user selects.
Mount points become available in the opera.io.filesystem.mountPoints property. This object is a File object.
The mountpoint URL protocol
In some cases, you will want your application to be able to reference files that have been mounted in the virtual file system from a web page. You can use the mountpoint URL protocol for this purpose. A mountpoint URL starts with the text mountpoint://, followed by the name of a mount point and a path to a file under that mount point.
For example, if a user has added a mount point, and named it myImages using the call:
browseForDirectory("myImages","",callback_function);
the user can access files inside the mount point by creating an absolute URI:
<img src="mountpoint://myImages/avatar.png">
Special mount points: the application and storage directories
There are two special directories you can use with the File I/O API:
- The application directory, which contains the actual files in the application accessing the file system. If the application is a widget, this directory will contain all the files in the widget, like config.xml, index.html and others. This directory is always mounted as readonly.
- The private storage directory which can be used to save files specific to the application. The files stored in this directory persist until the application is uninstalled.
These are not available by default and need to be mounted using the FileSystem.mountSystemDirectory() method:
opera.io.filesystem.mountSystemDirectory('application');
opera.io.filesystem.mountSystemDirectory('storage','myCoolSaves');
Once mounted, they become available under the opera.io.filesystem.mountPoints property.
You may specify an optional name to mount these directories as. If not supplied, their names default to 'application' and 'storage' respectively.
In the example above application directory will be mounted as application and have a path '/application', while storage will be mounted as myCoolSaves and have the path '/myCoolSaves'.
These can then be accessed as regular mount points and through the mountpoint URL protocol as with other mounted files, except that the application directory is mounted as readonly.
Paths
Note that the path separator is always '/', regardless of the underlying file system.
The fileSystem.mountPoints property represents the root of the file system and has the path '/'.
A mount point mounted with the name foo has the path '/foo'.
All files belong to only one mount point, so if a directory mounted as 'foo' has a file called 'bar', the path of the file is '/foo/bar'.
Paths that begin with '/' are absolute paths, starting from the root and moving down through a mount point, through any subdirectories and potentially to a file.
You may use relative paths. Any path not starting with a '/' is a relative path. The '.' and '..' directory references are supported. Paths are relative to the current directory. If file is a regular file, and you call file.moveTo('some/path') or similar methods, the path is relative to the parent directory of file. If file is a directory, the path is relative to that directory.
Working with files
You obtain an initial file by adding a mount point as described earlier. From here you have two options:
- If the mount point is a directory, you can move into its content as described in the next section.
- If the mount point is a file, you can manipulate it as described in this section.
You can use the resolve() method on the initial File object to get a reference to a File somewhere under the mount point. This method takes a path as an argument and will attempt to resolve it. If the path is valid, an File object is returned. Note that the file does not need to exist, the path simply needs to be a possibly valid path.
var file = mp.resolve('path/to/my/file');
Note that the path separator is always '/', regardless of the underlying file system.
Some important properties of the File object:
- exists
- Check if the file referenced by this File object actually exists. Especially useful when using the
resolve()method. - isFile
- If the File object references a regular file.
- isDirectory
- If the File object references a directory.
- created
- When the file was created.
- modified
- When the file was last modified.
- path
- The path to this file in the virtual file system, starting with '/' and the name of the mount point.
You may copy and move files by using the copy and moveTo methods:
file.copyTo('path/to/copy');
file.moveTo('new/name/of/file');
Both methods take an optional argument overwrite which will cause any existing files with the new path to be overwritten.
To create a new directory, use the following syntax:
var file = mountPoint.createDirectory(somePath);
var file = mountPoint.createDirectory(mountPoint.resolve(somePath));
In order to write files, you need to open a FileStream to the file and write to it. See the section on working with streams.
To delete files or directories, use the deleteFile() or deleteDirectory() methods:
mp.deleteFile('path/to/file');
mp.deleteDirectory('path/to/directory', true);
Both methods may take a File object instead of a path. The second argument is to delete content recursively
Working with directories
A File object made from a directory points to its subdirectories and contained files. The File object supports a length property and an array-like syntax to access these subfiles. Note that the subfiles and directories need to be 'refreshed' before you can actually access them. Through this process, information about the files in the directory are loaded into the virtual filesystem. The method refresh() is used for thispurpose:
dir.refresh(); //Load the contents of the directory
for ( var i = 0, file; file = dir[i]; i++ )
{
opera.postError(file.path + ' ' + file.isDirectory + ' ' file.isFile);
}
When the file is a directory, its length property will tell you how many files and subdirectories there are in the directory.
It's important to note that information about the subfiles and directories of this directory is not live. If files are added, moved or deleted, you need to call refresh() again to update the information in the File object.
You can similarly recurse through the file structure.
Reading and writing: Working with files streams
In order to read or write to a file, you need to make a File object and then open it for reading or writing using the open method:
var stream = dir.open('newfile', 'w');
stream.writeLine('hello');
stream.close();
stream = dir.open('newfile');
var data = stream.readLine();
opera.postError(data);
Using 'w'; will overwrite all data in the file. Use 'a' to append data instead. If the file does not exist, it is immediately created when opened in either of these modes.
The following modes are supported:
r- Open for reading only; place the file pointer at the beginning of the file.
r+- Open for reading and writing; place the file pointer at the beginning of the file.
w- Open for writing only; place the file pointer at the beginning of the file and truncate the file to zero length. If the file does not exist, attempt to create it.
w+- Open for reading and writing; place the file pointer at the beginning of the file and truncate the file to zero length. If the file does not exist, attempt to create it.
a- Open for writing only; place the file pointer at the end of the file. If the file does not exist, attempt to create it.
a+- Open for reading and writing; place the file pointer at the end of the file. If the file does not exist, attempt to create it.
You may write characters, lines, Base64-encoded text and images to the stream, using the different writeX() methods of the FileStream object.

