Even though web apps are becoming more and more offline capable, local file access (which is the only thing available when you're offline) is still needlessly restricted and limited. After the user has consented to give the app access to a file(s) from their local file system, the web app's read access to that file is limited until a reload and there is no write access at all.
To work around the read issue, a user would have to never refresh the app as otherwise they risk the inconvenience of having to repick all the files again. The app could put the File
objects in IndexedDB, but this both duplicates the data, uses up the app's storage limits (very quickly with media files I might add) and doesn't address the write case at all. IndexedDB is by design unsuitable for this [1]: https://code.google.com/p/chromium/issues/detail?id=476959#c6 [2]: https://code.google.com/p/chromium/issues/detail?id=108012#c69.
To work around the write issue, the user is made to open a 'save as' dialog (using <a href="bloburl" download>
), find the file they are modifying and overwrite. The web cannot currently do 'save' at all, even though it is more common action in native applications.
Better UX for any kind of file editing task. For example: Drop an image to an app, rotate it and you're already done without having to go through the painful 'save as' dialog process.
Media player apps need persistent access to media files the user has added. The IndexedDB workaround is not even possible here because media libraries are huge, so you basically have to read your entire media library every time the app is started.
Implement FileReference
object that can be obtained from a native (as opposed to JS constructed) File
object. FileReference
represents a reference to a File
rather than its contents. It grants the app a permission to access the referenced file as long as the file has not been modified from the point it was originally obtained. The FileReference
object has internal fields to keep track of this (see Privacy and Security).
The FileReference
object can be persisted in IndexedDB, so that access to files can be regained across page refreshes without user intervention. Because the FileReference
is just a pointer, the IndexedDB transaction semantics don't need to worry about the file's contents at all.
API:
FileReference(File file)
-> FileReference
. Constructs a new file reference from file
. If the file
is not a native File
with internal file reference to actual file system file, throw a type error. The file reference's internal write permission field is set to false.
FileReference.prototype.getFile()
-> Promise<File>
. Returns a promise that resolves to a File
object exactly as if the user had selected the File
with a file picker. If the file reference is invalid (file has been modified outside the app's control), the promise is rejected with ObsoleteFileReferenceError
.
FileReference.prototype.update(bytes Blob|ArrayBuffer, [, start ])
-> Promise<void>
. Modify the file by writing bytes
starting at start
or 0
. If the file reference is invalid (file has been modified outside the app's control), the promise is rejected with ObsoleteFileReferenceError
. If the file reference's write permission field is false
, prompt for a permission for the write ("Site wants to make changes to C:\My Documents\todo.txt, yes/no?"). If permission is denied, reject the promise with FileNotWritable error (or PermissionDeniedError?).
Blob.prototype.saveAs([defaultFileName])
-> Promise<FileReference>
. Starts a "save as" dialog with defaultFileName
as suggested file name and allowed extension based on the blob's mime type. If the dialog and write succeeds, resolve the promise with a FileReference
that references the newly created file. This FileReference has internal write permission set to true
.
When a file reference is persisted to IndexedDB, its internal write permission is always persisted as false
. This means a permission to change a file is only good for current session. FileReference
s can only be obtained in privileged context (Https-only, like Service Worker).
Persistent read should not have any new privacy or security issues as the Blob URL and File apis are already guarding against outside modifications. The user can get rid of FileReference
s any time by clearing IndexedDB data.
In-place writing to file definitely needs a permission. Writing should not be allowed on system files etc (are those even allowed to be read?). I think write permission should probably be on per-directory basis but not sure if that's too much trouble.
It would be great to rationalize this with http://w3c.github.io/filesystem-api/ although that's not a prerequisite. As was done for Directory Upload it'd be useful to carve off the use cases (writable files, files-by-references) and tackle those first.
Agreed that "per-directory basis" or similar scoping is likely necessary to make this safe, but not sure that that's enough. Given that users don't read permission prompts, there are big UX and privacy/security challenges here.