GoogleChromeLabs / browser-fs-access

File System Access API with legacy fallback in the browser
https://googlechromelabs.github.io/browser-fs-access/demo/
Apache License 2.0
1.38k stars 84 forks source link

Implement `getAsFileSystemHandle` #84

Closed omgoshjosh closed 2 years ago

omgoshjosh commented 2 years ago

Regarding getAsFileSystemHandle, I found this issue https://github.com/GoogleChromeLabs/browser-fs-access/issues/22#issuecomment-791636231.

Is there any plan to support this (with types)?

I would use native-file-system-adapter were it not for its lack of typescript types.

tomayac commented 2 years ago

I fear this can't be implemented, since there is no polyfill for the legacy approach. Here's a code snippet from an actual project of mine where I deal with this:

import { supported } from 'browser-fs-access';

document.addEventListener('drop', async (event) => {
  event.preventDefault();
  event.stopPropagation();
  const item = event.dataTransfer.items[0];
  if (item.kind === 'file') {
    let blobURL;
    if (supported) {
      const handle = await item.getAsFileSystemHandle();
      if (handle.kind !== 'file') {
        return;
      }
      const file = await handle.getFile();
      blobURL = URL.createObjectURL(file);
      // Do something with `handle`, for example, store it.
      // [Snip]
      return;      
    }
    const file = item.getAsFile();
    blobURL = URL.createObjectURL(file);
    // [Snip]
  }
});
omgoshjosh commented 2 years ago

Thanks for the quick response! Just for more context, I'm trying to recreate a directory structure found from files, which I believe this library does under the hood by splitting the path and recursively entering directories to find more files.

I guess I kinda just have to do that myself, eh?

Can you expound a bit on "no polyfill for legacy, therefore cannot implement"?

From the other library, I see this:

if (globalThis.DataTransferItem && !DataTransferItem.prototype.getAsFileSystemHandle) {
  DataTransferItem.prototype.getAsFileSystemHandle = async function () {
    const entry = this.webkitGetAsEntry()
    const [
      { FileHandle, FolderHandle },
      { FileSystemDirectoryHandle },
      { FileSystemFileHandle }
    ] = await Promise.all([
      import('./adapters/sandbox.js'),
      import('./FileSystemDirectoryHandle.js'),
      import('./FileSystemFileHandle.js')
    ])

    return entry.isFile
      ? new FileSystemFileHandle(new FileHandle(entry, false))
      : new FileSystemDirectoryHandle(new FolderHandle(entry, false))
  }
}

it just uses webkitGetAsEntry for drag drop data transfer to determine whether it should be a file or directory handle.

Is there a reason something like this wouldn't work for browser-fs-access?

Is it because of the reliance on webkitGetAsEntry and the fact that browser-fs-access doesn't provide any underlying implementations for any of the following?

FileSystemHandle.js FileSystemFileHandle.js FileSystemDirectoryHandle.js

I want to future proof my project and improve our folder picker to behave like file drop (detect and create remote directories based on the directory the user has dropped or selected).

It seems like these two features just need to have two different implementations? One that uses the webkitGetAsEntry for "file drop" and... i guess one that uses getAsFileSystemHandle for "pick directory"?

is there like high level guidance for what to do to support those two? is it just that these two use cases for handling file uploads aren't yet supported as a unified "FileSystemHandle"?

tomayac commented 2 years ago

@jimmywarting's library implements the interface of the File System Access (FSA) API as closely as possible, whereas my library provides a higher-level abstraction on top of either the legacy or the FSA implementation. If you want to stay as close to the metal as possible, then I recommend going with Jimmy's drag & drop solution. As noted in Jimmy's README, storing a fake file handle in IndexedDB or sharing it with postMessage isn't currently possible.