Skip to content

Instantly share code, notes, and snippets.

@petkaantonov
Last active January 17, 2019 20:32
Show Gist options
  • Save petkaantonov/37a9c64ea2bbd1aa3ca5 to your computer and use it in GitHub Desktop.
Save petkaantonov/37a9c64ea2bbd1aa3ca5 to your computer and use it in GitHub Desktop.

Better Web File Access

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.

Use cases

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.

Proposal

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. FileReferences can only be obtained in privileged context (Https-only, like Service Worker).

Privacy and Security

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 FileReferences 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.

@inexorabletash
Copy link

FYI, I'm still a fan! Planning to move forward in this space on the spec and implementation front, albeit slowly.

@petkaantonov
Copy link
Author

Awesome. Anywhere I can follow the progress?

@forresthopkinsa
Copy link

Hi -- this is a much-needed feature. Did this discussion continue elsewhere? Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment