browser-fs-access

repository·main·Indexed 23 days ago

https://github.com/googlechromelabs/browser-fs-access

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

Tokens
3.1K
Snippets
7
Records
18
Agent score
80%

What's inside browser-fs-access

  1. How browser-fs-access works: File System Access API with fallback

    main
    The module provides a unified interface for file operations. It feature-detects support for the File System Access API and uses it when available. If the API is not supported, it transparently falls back to legacy methods like <input type="file"> for opening files and <a download> for saving files. This makes it a ponyfill for progressive enhancement.
  2. Check File System Access API support

    main

    Use 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.');
    }
  3. Open a file with fileOpen()

    main

    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'],
      },
    ]);
  4. Configure fileSave() options and handles

    main

    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
    );
  5. Configure directoryOpen() options

    main

    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);
  6. Configure fileOpen() options

    main

    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);
  7. Use legacySetup for cleanup and manual rejection

    main

    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.');
          }
        };
      },
    });
  8. Open directories with directoryOpen()

    main

    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.