File System Access API: Recursive Directory Traversal

Posted . Visible to the public.

The File System Access API Show archive.org snapshot is a new capability of modern browsers that allows us to iterate over selected folders and files on a user's machine. Browser support is not great yet Show archive.org snapshot , but if the feature is only relevant for e.g. a single admin user it could still be worth using it prior to wider adaption instead of building yet another ZIP upload form.

Below is a simple compiler that i used to evaluate this feature.

Image

%button(up-folder-upload)
  %span Select Folder to Upload
up.compiler('[up-folder-upload]', (element) => {
  up.on(element, 'click', async (event) => {
    event.preventDefault()

    if (!('showDirectoryPicker' in window)) {
      alert('Your browser does not support the File System Access API. Please try a modern browser like Chrome or Edge.')
      return
    }

    const processDirectory = async (dirHandle, pathPrefix, prefix = '') => {
      const entries = []
      for await (const entry of dirHandle.values()) {
        entries.push(entry)
      }
      entries.sort((a, b) => {
        if (a.kind === b.kind) {
          return a.name.localeCompare(b.name)
        }
        return a.kind === 'directory' ? -1 : 1
      })
      for (const entry of entries) {
        const connector = '├─'
        const childPrefix = prefix + '│  '

        if (entry.kind === 'file') {
          const file = await entry.getFile()
          const fullPath = `${pathPrefix}/${file.name}`

          console.groupCollapsed(`${prefix}${connector} 📄 ${file.name}`)
          console.log(`Full Path: ${fullPath}`)
          console.log(`Last Modified: ${new Date(file.lastModified)}`)
          console.log(`Size: ${file.size} bytes`)
          console.groupEnd()
        } else if (entry.kind === 'directory') {
          const newPathPrefix = `${pathPrefix}/${entry.name}`

          console.groupCollapsed(`${prefix}${connector} 📁 ${entry.name}`)
          await processDirectory(entry, newPathPrefix, childPrefix)
          console.groupEnd()
        }
      }
    }

    const directoryHandle = await window.showDirectoryPicker()
    console.groupCollapsed(`📁 ${directoryHandle.name}`)
    await processDirectory(directoryHandle, directoryHandle.name)
    console.groupEnd()
  })
})
Profile picture of Michael Leimstädtner
Michael Leimstädtner
Last edit
Michael Leimstädtner
License
Source code in this card is licensed under the MIT License.
Posted by Michael Leimstädtner to makandra dev (2025-11-03 13:17)