Client-side file handling

The SiteFusion client is based on webbrowser technology, but unlike a web-application there are no restrictions in accessing files and running executables on the client-side. Through the ClientFileService class all these actions can be initiated. The ClientFileService class is a Node, and thus needs to be added to the node tree of your application to be able to function:


= new ClientFileService();
$this->window->addChild$cfs );

$cfs->writeFileFromString"C:\\hello.txt""Hello World!" );

All methods of ClientFileService are asynchronous. This means that they will not block your code but instead rely on a callback mechanism to inform you when the operation ended and what the result was. The callback is not required, which could make sense in the above example, if the caller would not care whether the writing of the file was successful. However when reading anything, files or directory listings, you have to specify a callback to receive the data.


->readFileToString"C:\\hello.txt"$this"readHelloCallback" );

The callback function could then look like this:

// Assume Application class context here

public function readHelloCallback$event ) {
$event->name == "finished" ) { // No errors
$fileToStringUploader $event->sourceObject;
$this->window->alert$fileToStringUploader->data );

the $fileToStringUploader variable in the above example function refers one of the several uploader and downloader classes that the file handling API contains. These classes are used to manage longer operations separately and to inform the program on progress of the operation. They are constructed by the ClientFileService and returned from the associated functions readFile, readFileToStream, readFileToString, writeFile, writeFileFromStream and writeFileFromString.

The downloader classes are FileDownloader, FileFromStreamDownloader and FileFromStringDownloader. The uploader classes are FileUploader, FileToStreamUploader and FileToStringUploader. These objects all fire the following events:
  • started - fires when the transfer is started. Handler receives the path (string) as the second argument.
  • cancelled - fires when the transfer is cancelled by the cancelDownload or cancelUpload methods. Handler receives the path (string) as the second argument.
  • failed - fires when the transfer failed. Handler receives the path (string) as the second argument.
  • finished - fires when the transfer successfully finished. Handler receives the path (string) as the second argument.
  • cycle - fires for every chunk of data successfully transferred. Handler receives the the filesize (integer) as the second argument, the total amount of bytes transferred (integer) as the third argument and the cycle number (integer) as the fourth argument.
Progress checking is allowed through the cycle event. By setting an event handler to this event, the interface can be updated to show the progress to the user:


$cfs->readFile"C:\\hello.txt""/tmp/hello.txt" );
$fileUploader->setEventHandler'cycle'$this'onProgress' );

// Then define the event handler:

public function onProgress$event$filesize$progress$cycle ) {
$someProgressMeter->value( (int) (($progress/$filesize)*100) );

Working with directories

Client-side directories can be read and have a serverside representation in the ClientDirectory class. The files and directories they contain are placed in the ClientDirectory::$entries array as ClientFile and ClientDirectory objects. It's easy to iterate over the contents of a directory:


// Read a specific directory...
$cfs->getDirectory"C:\\test"$this'onDirectoryRead' );
// ... or a special directory like the user's desktop:
$cfs->getSpecialDirectory"Desk"$this'onDirectoryRead' );

// Then define the handler:

public function onDirectoryRead$fileService$status$dirFile ) {
$files = array();
$directories = array();
$status ) {
        foreach ( 
$dirFile->entries as $entry ) {
$entry->isDirectory )
$directories[] = $entry;
$files[] = $entry;

Sub-directories are not read recursively. To get the contents of a sub-directory, call the ClientDirectory::get() method.

To create a new directory, you first need to create the ClientDirectory representation of this directory, and then call the ClientDirectory::create() method:


// Get the desktop
$cfs->getSpecialDirectory"Desk"$this'onDesktopRead' );

// Then define the handler:

public function onDesktopRead$fileService$status$desktopDir ) {
$helloDir $desktopDir->appendDirectory'hello' );

Similarly, directories and files can be removed my calling the ClientFile::remove() and ClientDirectory::remove() methods, or through the ClientFileService directly with ClientFileService::removeDirectory() and ClientFileService::removeFile().

Executing and monitoring files

Executable files can be launched through the ClientFileService as well. The direct method is by calling ClientFileService::executeFile(). When in a context of having a ClientFile at hand, it can be executed by calling the ClientFile::execute() method. In both cases, the execute method can take an optional arguments array $args, which can be supplied as an empty array to call the executable without arguments, and a boolean $async, which indicates whether the application should block (FALSE) or continue (TRUE) until the executable ends.


->executeFile"C:\\test.exe", array(), TRUE );

Monitoring files can be similarly achieved by calling ClientFile::monitor() or ClientFileService::monitorFile(). These methods require a callback handler to be specified. This callback handler get called whenever the state of the file changes. It is supplied with information on whether the file exists, what size it is and what its modification time is.

Using the native XULFilePicker

XUL supplies the file picker to enable users to select single or multiple files and directories on their local filesystem. This filepicker is featured as the OS-native file picker on all operating systems. In the constructor of XULFilePicker, the title, picker mode, default path and default filename extension can be specified. Picker mode is a string and should be one of the following:
  • "open" - open a single file
  • "save" - save a file
  • "getfolder" - select a folder
  • "openmultiple" - open multiple files
After constructing the filepicker and adding it to the node tree, it can be opened by calling the XULFilePicker::open() method:


= new XULFilePicker'Select a file''open' );
$this->window->addChild$fp );

The filepicker fires a closed event when it is dismissed. Attach an event handler to this event to initiate action:


->setEventHandler'closed'$this'onFilePickerReturn' );

// Then define the handler:

public function onFilePickerReturn$event ) {
$fp $event->sourceObject;
    switch ( 
$fp->returnCode ) {
// User confirmed, the path is in $fp->file
            // or multiple paths in the $fp->files array

// User cancelled

// This only happens we're saving a file and the
            // user selected an already existing path. The
            // user has confirmed that the file may be overwritten,
            // so be prepared to do so.
            // Resulting path is in $fp->file

Filters can be added to a XULFilePicker before it is opened in order to determine what file types and extensions are to be shown in the directory views and as the format options for saving.


->addFilter("Images""*.jpg; *.gif; *.png; *.");
$fp->addFilter("Documents""*.doc; *.docx; *.txt; *.");

Comment on this tutorial
Copy the code: