This specification defines conformance criteria that apply to a single product: a user agent that implements the interfaces that it contains.
This API is intended to satisfy client-side-storage use cases not well served by databases. These are generally applications that involve large binary blobs and/or share data with applications outside of the browser.
It is intended to be minimal in extent, but sufficiently powerful that easy-to-use libraries may be built on top of it.
// In the DOM or worker context: function useAsyncFS(fs) { // see getAsText example in [[!FILE-API]]. fs.root.getFile("already_there.txt", null, function (f) { getAsText(f.file()); }); // But now we can also write to the file; see [[!FILE-WRITER]]. fs.root.getFile("logFile", {create: true}, function (f) { f.createWriter(writeDataToLogFile); }); } requestFileSystem(TEMPORARY, 1024 * 1024, function(fs) { useAsyncFS(fs); }); // In a worker: var tempFS = requestFileSystem(TEMPORARY, 1024 * 1024); var logFile = tempFS.root.getFile("logFile", {create: true}); var writer = logFile.createWriter(); writer.seek(writer.length); writeDataToLogFile(writer);
DOMError is defined in the DOM4 specification [[!DOM4]].
File is defined in the File API specification [[!FILE-API]].
FileWriter and FileWriterSync are defined in the FileWriter specification [[!FILE-WRITER]].
An application can request temporary or persistent storage space. Temporary storage may be easier to get, at the UA's discretion [looser quota restrictions, available without prompting the user], but the data stored there may be deleted at the UA's convenience, e.g. to deal with a shortage of disk space.
Conversely, once persistent storage has been granted, data stored there by the application should not be deleted by the UA without user intervention. The application may of course delete it at will. The UA should require permission from the user before granting persistent storage space to the application.
This API specifies the standard origin isolation in a filesystem context, along with persistence of data across invocations. Applications will likely use temporary storage for caching, and if it's still around from a previous session, it is often useful. Persistent data, on the other hand, is useless if you can't access it again the next time you're invoked. However, even persistent data may be deleted manually by the user [either through the UA or via direct filesystem operations].
requestFileSystem
MUST have the following properties:
Because this API may allow untrusted code to read and write parts of a user's hard drive, there are a number of security and privacy issues that must be dealt with. Risks to the user include:
As with any other client-side storage, filesystem access allows for cookie-resurrection attacks. UAs will likely wish to present the option of clearing it when the user clears any other origin-specific storage, blocking access to it when cookies are blocked, etc. This is especially important if temporary storage space is permitted by default without explicit user permission.
There are several ways in which a file system entry point can be obtained. Not all user agents may in fact implement all of them. However, in order to avoid blocking UI actions while waiting on filesystem IO, we define only an asynchronous interface for Window, and restrict the synchronous API to the Worker context defined in [[!WEBWORKERS]].
LocalFileSystem
Requests a filesystem in which to store application data.
If successful, this function MUST give access to an origin-private filesystem, as defined above.
TEMPORARY
or
PERSISTENT
.
Allows the user to look up the Entry for a file or directory referred to by a local URL.
LocalFileSystemSync
Requests a filesystem in which to store application data.
If successful, this function MUST give access to an origin-private filesystem, as defined above.
TEMPORARY
or
PERSISTENT
.
Allows the user to look up the Entry for a file or directory referred to by a local URL.
Metadata
interfaceThis interface supplies information about the state of a file or directory.
Flags
dictionaryThis dictionary is used to supply arguments to methods that look up or create files or directories.
exclusive
MUST have no effect. Used with
create
, it MUST cause getFile and getDirectory to
fail if the target path already exists.
// Get the data directory, creating it if it doesn't exist. dataDir = fsSync.root.getDirectory("data", {create: true}); // Create the lock file, if and only if it doesn't exist. try { lockFile = dataDir.getFile("lockfile.txt", {create: true, exclusive: true}); } catch (ex) { // It already exists, or something else went wrong. ... }
FileSystem
interfaceThis interface represents a file system.
Entry
interfaceAn abstract interface representing entries in a file system, each of which may be a File or DirectoryEntry.
Look up metadata about this entry.
Move an entry to a different location on the file system. It is an error to try to:
Copy an entry to a different location on the file system. It is an error to try to:
Returns a URL that can be used to identify this entry. Unlike the URN defined in [[!FILE-API]], it has no specific expiration; as it describes a location on disk, it should be valid at least as long as that location exists.
Do we want to spec out the URL format/scheme? It would be quite nice if these could be edited and manipulated easily, as with normal filesystem paths.
How and where can these URLs be used? Can they be interchangeable with online URLs for the same domain?
Proposal currently under discussion:
filesystem:http://example.domain/persistent-or-temporary/path/to/file.html
.Deletes a file or directory. It is an error to attempt to delete a directory that is not empty. It is an error to attempt to delete the root directory of a filesystem.
DirectoryEntry
interfaceThis interface represents a directory on a file system.
Creates a new DirectoryReader to read Entries from this Directory.
Creates or looks up a file.
create
and exclusive
are both
true, and the path already exists, getFile MUST fail.create
is true, the path doesn't exist,
and no other error occurs, getFile MUST create it as a
zero-length file and return a corresponding FileEntry.create
is not true and the path doesn't
exist, getFile MUST fail.create
is not true and the path exists,
but is a directory, getFile MUST fail.Creates or looks up a directory.
create
and exclusive
are both
true and the path already exists, getDirectory MUST fail.create
is true, the path doesn't exist,
and no other error occurs, getDirectory MUST create and return
a corresponding DirectoryEntry.create
is not true and the path doesn't
exist, getDirectory MUST fail.create
is not true and the path exists,
but is a file, getDirectory MUST fail.Deletes a directory and all of its contents, if any. In the event of an error [e.g. trying to delete a directory that contains a file that cannot be removed], some of the contents of the directory MAY be deleted. It is an error to attempt to delete the root directory of a filesystem.
DirectoryReader
interfaceThis interface lets a user list files and directories in a directory. If there are no additions to or deletions from a directory between the first and last call to readEntries, and no errors occur, then:
Read the next block of entries from this directory.
FileEntry
interfaceThis interface represents a file on a file system.
Creates a new FileWriter associated with the file
that this FileEntry
represents.
Returns a File that represents the current state of
the file that this FileEntry
represents.
Several calls in this API are asynchronous, and use callbacks.
FileSystemCallback
interface
When requestFileSystem()
succeeds, the following
callback is made:
The file system was successfully obtained.
EntryCallback
interfaceThis interface is the callback used to look up Entry objects.
EntriesCallback
interface
When readEntries()
succeeds, the following callback is
made.
MetadataCallback
interfaceThis interface is the callback used to look up file and directory metadata.
Used to supply file or directory metadata as a response to a user query.
FileWriterCallback
interfaceThis interface is the callback used to create a FileWriter.
Used to supply a FileWriter as a response to a user query.
FileCallback
interfaceThis interface is the callback used to obtain a File.
Used to supply a File as a response to a user query.
VoidCallback
interfaceThis interface is the generic callback used to indicate success of an asynchronous method.
ErrorCallback
interfaceWhen an error occurs, the following callback is made:
There was an error with the request. Details are provided by the
err
parameter.
FileSystemSync
interfaceThis interface represents a file system.
EntrySync
interfaceAn abstract interface representing entries in a file system, each of which may be a FileEntrySync or DirectoryEntrySync.
Look up metadata about this entry.
Move an entry to a different location on the file system. It is an error to try to:
Copy an entry to a different location on the file system. It is an error to try to:
Returns a URL that can be used to identify this entry. Unlike the URN defined in [[!FILE-API]], it has no specific expiration; as it describes a location on disk, it should be valid at least as long as that location exists.
Deletes a file or directory. It is an error to attempt to delete a directory that is not empty. It is an error to attempt to delete the root directory of a filesystem.
DirectoryEntrySync
interfaceThis interface represents a directory on a file system.
Creates a new DirectoryReaderSync to read EntrySyncs from this DirectorySync.
Creates or looks up a file.
create
and exclusive
are both
true and the path already exists, getFile MUST fail.create
is true, the path doesn't exist,
and no other error occurs, getFile MUST create it as a
zero-length file and return a corresponding FileEntry.create
is not true and the path doesn't
exist, getFile MUST fail.create
is not true and the path exists,
but is a directory, getFile MUST fail.Creates or looks up a directory.
create
and exclusive
are both
true and the path already exists, getDirectory MUST fail.create
is true, the path doesn't exist,
and no other error occurs, getDirectory MUST create and return
a corresponding DirectoryEntry.create
is not true and the path doesn't
exist, getDirectory MUST fail.create
is not true and the path exists,
but is a file, getDirectory MUST fail.Deletes a directory and all of its contents, if any. In the event of an error [e.g. trying to delete a directory that contains a file that cannot be removed], some of the contents of the directory MAY be deleted. It is an error to attempt to delete the root directory of a filesystem.
DirectoryReaderSync
interfaceThis interface lets a user list files and directories in a directory. If there are no additions to or deletions from a directory between the first and last call to readEntries, and no errors occur, then:
Read the next block of entries from this directory.
FileEntrySync
interfaceThis interface represents a file on a file system.
Creates a new FileWriterSync associated with the file
that this FileEntrySync
represents.
Returns a File that represents the current state of
the file that this FileEntrySync
represents.
Error conditions can occur when attempting to write files. The list below of potential error conditions is informative, with links to normative descriptions of errors:
An operation on a file may fail due to the file [or a parent directory] having been removed before the operation is attempted. See NotFoundError.
An operation on a file may not make sense, e.g. moving a directory into one of its own children. See InvalidModificationError.
An operation on a file may not make sense if the underlying filesystem has had changes made since the reference was obtained. See TypeMismatchError, InvalidStateError.
Users may accidentally attempt to create a file where another already exists. See PathExistsError.
Synchronous methods MUST throw an exception of the most appropriate type in the table below if there has been an error with writing.
If an error occurs while processing an asynchronous method,
the err
argument to the ErrorCallback MUST
be a DOMError object [[!DOM4]] of the most appropriate
type from the table below.
Name | Description |
---|---|
EncodingError | A path or URL supplied to the API was malformed. |
InvalidModificationError | The modification requested was illegal. Examples of invalid modifications include moving a directory into its own child, moving a file into its parent directory without changing its name, or copying a directory to a path occupied by a file. |
InvalidStateError |
An operation depended on state cached in an interface object,
but that state that has changed since it was read from disk.
Which values will actually go stale? Modification
time, name, more rarely type. If an atomic save [replacing
a file with a new one of the same name] happens underneath,
should we even be required to notice?
|
NotFoundError | A required file or directory could not be found at the time an operation was processed. |
NotReadableErr | A required file or directory could be read. |
NoModificationAllowedError | The user attempted to write to a file or directory which could not be modified due to the state of the underlying filesystem. |
PathExistsError | The user agent failed to create a file or directory due to the existence of a file or directory with the same path. |
QuotaExceededError | The operation failed because it would cause the application to exceed its storage quota. |
SecurityError |
|
TypeMismatchError | The user has attempted to look up a file or directory, but the Entry found is of the wrong type [e.g. is a DirectoryEntry when the user requested a FileEntry]. |
In order to make it easy for web app developers to write portable applications that work on all platforms, we enforce certain restrictions and make certain guarantees with respect to paths used in this API.
Paths in this filesystem MUST be case sensitive and case-preserving.
Implementations MUST accept any valid UTF-8 sequence as a path segment, so long as it does not include any characters or sequences restricted below. When returning paths or path segments, implementations MUST return them in the same normalization in which they were presented.
File and directory names MUST NOT contain either of the following characters:
There's been discussion on whether backslash '\' (U+005c) should be disallowed or not. In favor of leaving it in is that it's a legal character on some filesystems, and it seems somewhat arbitrary to remove it. Opposed is that it may cause confusion, and in at least some cases complicates implementation.
The directory separator is '/' (U+002F).
The character '/', when it is the first character in a path, refers to the root directory.
All absolute paths begin with '/'; no relative paths begin with '/'.
A relative path describes how to get from a particular directory to a file or directory. All methods that accept paths are on DirectoryEntry or DirectoryEntrySync objects; the paths, if relative, are interpreted as being relative to the directories represented by these objects.
An absolute path is a relative path from the root directory, prepended with a '/'.
'.', when used where it is legal to use a directory name, refers to the currently-referenced directory. Thus 'foo/./bar' is equivalent to 'foo/bar', and './foo' is equivalent to 'foo'.
'..', when used where it is legal to use a directory name, refers to the parent of the currently-referenced directory. Thus, 'foo/..' refers to the directory containing 'foo', and '../foo' refers to an element named 'foo' in the parent of the directory on whose DirectoryEntry or DirectoryEntrySync the method receiving the path is being called.
What about path and path segment lengths? Should we limit them at all? It's hard to control the true length of a path, due to renames of parent directories, so we really just want to reject the obviously-too-long; they won't often really come up anyway. We should at least provide minimum lengths for paths and segments.
Should we limit the number of elements in a directory?
Thanks to Arun Ranganathan for his File API, Opera for theirs, and Robin Berjon both for his work on various file APIs and for ReSpec.