html5-fs

Node.js style wrapper for the HTML5/Cordova FileSytem API.

Usage no npm install needed!

<script type="module">
  import html5Fs from 'https://cdn.skypack.dev/html5-fs';
</script>

README

html5-fs

Maintainers/Contributors

If you have ideas and/or time to work on this project feel free to get in touch and I will be happy to collaborate. There be work to do!

A Node.js fs style FileSystem API Wrapper

The reason I chose write this module was due to the availability of FileSystem APIs in regular browsers and Cordova applications and the desire for an API consitent with that of the fs module in Node.js.

I prefer the Node.js callback style system when writing asynchronous code so this uses callbacks of the format function (err, result).

It's also CommonJS / RequireJS friendly thanks to wrapped using Browserify.

I initially began this module as a fork of GapFile, but started from scratch when I realised the two were radically different in style and aims.

Install

Use a file from the dist directory or do an npm install:

npm install html5-fs

Example vs. Standard File System API

This wrapper makes for shorter more concise code vs. the plain FileSystem API. It also has the added benefit of working with Node.js style libraries such as async which means it'll cause less headcahes when dealing with the asynchronous nature of the FileSystem API.

Here we use the plain old FileSystem API to create a folder and write a file into that created folder.


function writeFile(success, fail) {
  // fileSystem would be returned after some initialisation calls
  fileSystem.root.getDirectory('/test/', {
    create: true,
    exclusive: false
  }, function(dirEntry) {
      dirEntry.getFile('somefile.txt', {
        create: true,
        exclusive: false
      }, function(file) {
        file.createWriter(function(writer) {
          writer.onwrite = function(evt) {
              success(null);
          };

          writer.onerror = function(evt) {
              fail(evt.target.error);
          };

          writer.write(new Blob([data], {
            type: 'text/plain'
          }));
      }, fail);
    }, fail);
  }, fail);
}

The below code is equivalent to the previous code, but is using this wrapper which means it's far more concise and instantly recognisable if you've used the Node.js fs module:


var fs = require('html5-fs'); // Or use window.fs

function writeFile(callback) {
  fs.mkdir('test', function (err) {
    if (err) {
      callback(err, null);
    } else {
      fs.writeFile('test/somefile.txt', 'Hello world', callback);
    }
  });
}

You can then compose more aesthetically pleasing code using async:


var fs = require('html5-fs'); // Or use window.fs
var async = require('async'); // Or use window.async

function writeFile(callback) {
  function createDir(cb) {
    fs.mkdir('test', cb);
  }

  function writeFile(cb) {
    fs.writeFile('test/somefile.txt', 'Hello world', cb);
  }

  async.waterfall([
    createDir,
    writeFile
  ], callback);
}

Support

Currently Cordova applications, Chrome and new versions of Opera support this API.

This has only been tested on iOS and Android Cordova applications. It has also only been tested with version 1.3.2 of the org.apache.cordova.file plugin.

If using a Cordova application this library assumes you've configured your build with FileSystem access and the FileSystem plugin as described here.

Project Wide "fs" Override

You can map your project to use this module in place of the standard Node.js fs module by adding a browser object like so into the package.json before running the browserify command. Just make sure html5-fs is installed as a dependency first.


"browser": {
  "fs": "html5-fs"
}

For more info check out the Browserify Handbook.

Tests

First install all dependencies with npm install.

Also make sure you have Grunt installed npm install -g grunt-cli.

If you plan to test either iOS or Android you'll need to run cordova add platform [platform-name]. I did this using the Cordova CLI version 4.1.2.

If testing using the iOS Simulator you'll need to npm install -g ios-sim too.

To test using Android you'll need an emulator configured.

Now run grunt test. When the relevant browser(s) open allow file system access via the popup that appears (this is standard) and the tests will run.

To test Android run grunt test-android and for iOS use grunt test-ios.

Building & Contributing

All contributions are welcome! This project only implements a small subset of the Node.js FileSystem API. Naturally some functions may not be applicable or possible to implement due to the browser environment but having as a close a representation as possible to the original would be ideal.

To manage tests and builds Grunt is used. Running grunt test-[platform] will browserify the files in the /src directory into a single bundle with source maps enabled and then execute the tests using Karma or the applicable device emulator.

For production builds run grunt build. This command will browserify the source and write it to the /dist directory. It will also create a minified version.

API

All callbacks in this API follow the standard Node.js convention taking two parameters. An error followed by a result.

Errors returned do not match Node.js File System errors, if someone feels like mapping these be my guest, but it might be best to leave them as is!

fs.init(desiredBytes, callback)

Request access for a specific number of bytes. Users need to accept this request, usually via a popup in the browser. Cordova applications don't require users to accept any popups.

If no byte quota is provided then 10MB is requested.

Callback format should be as follows:

NOTE: for iOS and Android Cordova/PhoneGap applications the desiredBytes parameter is ignored as they expect a value of 0.

// Ask for a 5MB quota of persistent storage
fs.init(5 * 1024 * 1024, function(err) {
    if(err) {
      // Error handling
    } else {
      // Now we can use the other fs API functions!
    }
});

fs.writeFile(filename, data, callback)

Write data to a file specified by filename. data should be a string.

fs.appendFile(fullpath, data, callback)

Asynchronously append data to a file, creating the file if it doesn't exist.

fs.readFile(filename, [options], callback)

Read data from a file. options is an object that supports an encoding key that can have a value of "base64" or "utf8". By default "utf8" is assumed.

fs.unlink(path, callback)

Delete a file.

fs.readdir(dirname, callback)

Get a list of the files in a directory as an Array. Entries are instances of DirectoryEntry and have an isFile property that can be used to differentiate folders and files.

fs.mkdir(dirName, callback)

Create a directory. Currently this doesn't support creating a nested directory unless the parent already exists. In other words to create dir/subdir you'd need to first create dir then in a second call create subdir. This is the same behaviour as the Node.js fs module.

fs.rmdir(dirName, callback)

Delete a directory. It must be empty, just like with the Node.js rmdir function.

fs.exists(path, callback)

Test whether or not the given path exists by checking with the file system. Works just like the standard Node.js API meaning no error is passed to the callback.

fs.stat(path, callback)

Get metadata related to a file or directory. If the provided path doesn't end with a / character a file lookup is assumed. Result looks as follows:

{
    "size": 13,
    "modificationTime": "2014-04-26T17:01:11.000Z"
}

Changelog

  • 0.1.1

    • Fix from @vitalets for DOMError vs. FileError codes
    • Fix from @haadcode for fs.exists signature and bug
  • 0.1.0 - Use window.PERSISTENT instead of window.PERSISTENT_FLAG