Files
packages.dart/experimental/web_file_system/README.md
T

3.9 KiB

web_file_system

A high-performance, fully asynchronous VFS (Virtual File System) for the web implementing Dart's standard package:file interfaces.

It uses a hybrid backend that separates metadata management from actual block storage for optimal latency and performance in the browser.


Key Features

  • package:file Compliance: Drop-in replacement for standard file system operations. Fully compatible with libraries expecting a FileSystem interface.
  • O(1) Directory Renames: By utilizing an inode-based database structure, directory renames and moves are fast constant-time operations. Children don't require path prefix updates.
  • IndexedDB Metadata Store: Keeps directory hierarchies, timestamps, file sizes, and symbolic links indexed using fast IndexedDB tables.
  • OPFS (Origin Private File System) Block Store: Stores raw file bytes and stream chunks directly in the high-performance private browser storage space.
  • Symbolic Links (Symlinks): Full support for relative and absolute symbolic links, target updating, recursive listing, and canonical path resolution.

Architectural Flow

graph TD
    subgraph WebFileSystem ["WebFileSystem (Dart API)"]
        FS["WebFileSystem"]
        WD["WebDirectory"]
        WF["WebFile"]
        WL["WebLink"]
    end

    subgraph Metadata ["IndexedDB (IdbInodeService)"]
        DB[(WebFileSystemDB)]
        Inodes[Inode Table: parentId index]
    end

    subgraph DataBlocks ["OPFS (OpfsBlockStore)"]
        BlocksDir[/.blocks/ Directory]
        RawData[[UUID Data Blobs]]
    end

    FS -->|Lookup / Resolve| Metadata
    WD -->|Read / Write Children| Metadata
    WF -->|Read / Write Metadata| Metadata
    WF -->|Read / Write Data Blocks| DataBlocks
    WL -->|Read / Write Link Target| DataBlocks

Getting Started

Installation

Add web_file_system to your pubspec.yaml (or reference the path if using as a path dependency):

dependencies:
  web_file_system:
    path: path/to/web_file_system

Usage Examples

1. Initializing the File System

import 'package:web_file_system/web_file_system.dart';

final fs = WebFileSystem();

2. Creating and Reading Files

final file = fs.file('/documents/report.txt');

// Standard write & read (creates parent directories if needed when recursive is true)
await file.create(recursive: true);
await file.writeAsString('Hello, Web private storage!');

final contents = await file.readAsString();
print(contents); // "Hello, Web private storage!"

3. Directory Listing & Traversal

final dir = fs.directory('/documents');
await for (final entity in dir.list(recursive: true, followLinks: false)) {
  print('${entity.path} (${entity.runtimeType})');
}
// Create a symbolic link
final link = fs.link('/shortcut_to_report');
await link.create('/documents/report.txt');

// Reading content through the link
final data = await fs.file('/shortcut_to_report').readAsString();

// Resolve canonical absolute path
final canonicalPath = await link.resolveSymbolicLinks(); 
print(canonicalPath); // "/documents/report.txt"

Performance Notes: Inode vs Path-based Renames

Most browser file systems index files by their full path strings (e.g., keying a database table by /documents/photos/holiday.png). Renaming the directory /documents to /archive requires iterating over every nested path and rewriting their keys, which is an O(N) operation where N is the number of recursive items.

web_file_system implements an inode index model. Inodes reference parents by their database IDs rather than paths. Renaming a directory only updates the single directory node's name property. Its child files and directories remain untouched because they continue to reference the same unchanged parent inode ID. This makes directory renaming an $O(1)$ operation.