How browser-fs-access works: File System Access API with fallback
main<input type="file"> for opening files and <a download> for saving files. This makes it a ponyfill for progressive enhancement.repository·main·Indexed 23 days ago
https://github.com/googlechromelabs/browser-fs-accessA ponyfill providing a consistent API for file system access in the browser. It leverages the modern File System Access API where available and falls back to legacy <input> and <a> methods for broader compatibility. Key functions include fileOpen() for opening files, directoryOpen() for selecting directories, and fileSave() for saving data to the local file system. Version 0.38.0.
<input type="file"> for opening files and <a download> for saving files. This makes it a ponyfill for progressive enhancement.You can install the module using npm to add it to your project dependencies.
npm install --save browser-fs-accessUse the supported boolean to determine if the browser supports the modern File System Access API or if the library will be using the fallback implementation.
import { supported } from 'browser-fs-access';
if (supported) {
console.log('Using the File System Access API.');
} else {
console.log('Using the fallback implementation.');
}The fileOpen() function opens a file picker dialog. It returns a Blob (or an array of Blobs if multiple: true is set).
import { fileOpen } from 'browser-fs-access';
// Open a single image file
const blob = await fileOpen({
mimeTypes: ['image/*'],
});
// Open multiple image files
const blobs = await fileOpen({
mimeTypes: ['image/*'],
multiple: true,
});
// Open files with specific categories
const mixedBlobs = await fileOpen([
{
description: 'Image files',
mimeTypes: ['image/jpg', 'image/png', 'image/gif', 'image/webp'],
extensions: ['.jpg', '.jpeg', '.png', '.gif', '.webp'],
multiple: true,
},
{
description: 'Text files',
mimeTypes: ['text/*'],
extensions: ['.txt'],
},
]);directoryOpen() function allows users to select a directory. It returns an array of Blobs representing the files within that directory. The module polyfills the webkitRelativePath property on returned files for consistency.fileSave() function allows saving data to the local file system. It accepts a Blob, a Promise<Blob>, or a Response object (which will be streamed).The fileSave() function accepts an options object and an optional existingHandle for overwriting files using the File System Access API.
Options Reference:
fileName: Suggested file name to use, defaults to ''.extensions: Suggested file extensions, defaults to ''.startIn: Suggested directory in which the file picker opens.id: By specifying an ID, the user agent can remember different directories for different IDs.excludeAcceptAllOption: Include an option to not apply any filter in the file picker, defaults to false.Arguments:
blobOrResponseOrPromiseBlob: The data to save.options: Configuration object.existingHandle (optional): A FileHandle to save back to an existing file. This only works with the File System Access API. You can retrieve this from the handle property of a Blob received via fileOpen().throwIfExistingHandleNotGood (optional): A flag to determine whether to throw an error (rather than opening a new save dialog) when existingHandle is no longer valid (e.g., the file was deleted). Defaults to false.// Example of saving to an existing handle
const existingHandle = previouslyOpenedBlob.handle;
const throwIfExistingHandleNotGood = true;
await fileSave(
blobOrResponseOrPromiseBlob,
{ fileName: 'Untitled.txt', extensions: ['.txt'] },
existingHandle,
throwIfExistingHandleNotGood
);The directoryOpen() function accepts an options object.
Options Reference:
recursive: Set to true to recursively open files in all subdirectories, defaults to false.mode: Open the directory with "read" or "readwrite" permission, defaults to "read".startIn: Suggested directory in which the file picker opens.id: By specifying an ID, the user agent can remember different directories for different IDs.skipDirectory: Callback to determine whether a directory should be entered; return true to skip.const options = {
recursive: true,
mode: 'read',
startIn: 'downloads',
id: 'projects',
skipDirectory: (entry) => entry.name[0] === '.',
};
const blobs = await directoryOpen(options);The fileOpen() function accepts an options object or an array of option objects.
Options Reference:
mimeTypes: List of allowed MIME types, defaults to */*.extensions: List of allowed file extensions (with leading '.'), defaults to ''.multiple: Set to true for allowing multiple files, defaults to false.description: Textual description for file dialog, defaults to ''.startIn: Suggested directory in which the file picker opens (e.g., 'downloads', a well-known directory, or a file/directory handle).id: By specifying an ID, the user agent can remember different directories for different IDs.excludeAcceptAllOption: Include an option to not apply any filter in the file picker, defaults to false.const options = {
mimeTypes: ['image/*'],
extensions: ['.png', '.jpg', '.jpeg', '.webp'],
multiple: true,
description: 'Image files',
startIn: 'downloads',
id: 'projects',
excludeAcceptAllOption: true,
};
const blobs = await fileOpen(options);imageToBlob() function asynchronously converts an HTMLImageElement into a Blob object.The legacySetup option in FirstFileSaveOptions allows developers to manage cleanup and manual Promise rejection, which is particularly useful when using older browser APIs that rely on hidden anchor elements or require manual timeout management.
legacySetup receives resolve, rejectionHandler, and anchor. It must return a cleanup function that is called when the operation completes or is rejected.
const file = await fileOpen({
legacySetup: (resolve, rejectionHandler, anchor) => {
const timeoutId = setTimeout(rejectionHandler, 10_000);
return (reject) => {
clearTimeout(timeoutId);
if (reject) {
reject('My error message here.');
}
};
},
});Use directoryOpen() to allow users to select an entire directory. This returns an array of files and/or directories containing handles.
Options:
recursive: Whether to traverse subdirectories.startIn: A WellKnownDirectory or FileSystemHandle to suggest.id: A string to help the user agent remember the directory.mode: Set to 'readwrite' to request write access to the directory.skipDirectory: A callback (entry) => boolean that allows you to skip specific entries during the traversal.