fs_shim 2.5.4+2
fs_shim: ^2.5.4+2 copied to clipboard
A portable file system implementation working on io, web (OPFS or IndexedDB) and memory
fs_shim #
A portable file system library for Dart and Flutter.
fs_shim exposes a single asynchronous File/Directory/Link API (a subset
of dart:io) that runs the same code on:
- ๐ฅ Dart VM / Flutter native (IO)
- ๐ Web โ browser storage (through
idb_shim) - ๐ Web โ OPFS (Origin Private File System), a thin layer over the native browser API
- ๐งช Tests / in-memory (through
idb_shim)
API supported #
It contains a subset of the io File/Directory API. Basically all sync methods
are removed since on the web indexedDB (and OPFS) cannot be accessed in a
synchronous way. All operations are asynchronous and return a Future (or a
Stream for openRead/list).
Classes
File(create, openWrite, openRead, writeAsBytes, writeAsString, copy)Link(create, target)Directory(create, list)FileSystem(file, link, directory, type, isFile, isDirectory, isLink)FileSystemEntity(path, exists, delete, rename, absolute, isAbsolute, stat, parent)FileStatFileSystemEntityTypeFileSystemException
Static method
Directory.currentFileSystemEntity.isFileFileSystemEntity.isDirectoryFileSystemEntity.isLink
Static and File/Directory/Link constructors use fileSystemDefault which
is platform dependent (web or io).
Implementations #
| File system | Import | Platform | Links | Random access |
|---|---|---|---|---|
fileSystemDefault |
package:fs_shim/fs_shim.dart |
all | io | io / web |
fileSystemIo |
package:fs_shim/fs_shim.dart |
io | โ ยน | โ |
fileSystemMemory |
package:fs_shim/fs_shim.dart |
all | โ | โ |
fileSystemWeb |
package:fs_shim/fs_browser.dart |
web | โ | โ (opt-in) |
fileSystemOpfsWeb |
package:fs_shim/fs_opfs_web.dart |
web | โ | โ |
ยน File links are not supported on Windows (fs.supportsFileLink returns false).
You can always check capabilities at runtime with fs.supportsLink,
fs.supportsFileLink and fs.supportsRandomAccess.
Installation #
Add the following dependency to your pubspec.yaml:
dependencies:
fs_shim: ^<latest>
Usage #
In memory #
A simple usage example:
import 'package:fs_shim/fs_shim.dart';
import 'package:path/path.dart';
Future<void> main() async {
final fs = fileSystemMemory;
// Create a top level directory
final dir = fs.directory('/dir');
// and a file in it
final file = fs.file(join(dir.path, 'file'));
// create a file
await file.create(recursive: true);
await file.writeAsString('Hello world!');
// read a file
print('file: ${await file.readAsString()}');
// use a file link if supported
if (fs.supportsFileLink) {
final link = fs.link(join(dir.path, 'link'));
await link.create(file.path);
print('link: ${await fs.file(link.path).readAsString()}');
}
// list dir content
print(await dir.list(recursive: true, followLinks: true).toList());
}
The same code runs unchanged against any other implementation: just swap the
fs instance.
Using IO API #
Using fs_shim.dart
You can simply replace in the above example:
final fs = fileSystemMemory;
by
final fs = fileSystemIo;
If you only target io, you can still use the File and Directory
constructors, replace
import 'dart:io'
hide
Directory,
File,
Link,
FileSystemEntity,
FileMode,
FileStat,
OSError,
FileSystemException,
FileSystemEntityType;
import 'package:fs_shim/fs_shim.dart';
by
import 'package:fs_shim/fs_io.dart';
Then a reduced set of the IO API can be used, same source code that might require some cleanup if you import from existing code.
Simple example
import 'package:fs_shim/fs_io.dart';
import 'package:path/path.dart';
Future<void> main() async {
final fs = fileSystemDefault;
// safe place when running from package root
final dirPath = join(Directory.current.path, 'test_out', 'example', 'dir');
// Create a top level directory
final dir = Directory(dirPath);
print('dir: $dir');
// delete its content
if (await dir.exists()) {
await dir.delete(recursive: true);
}
// and a file in it
final file = File(join(dir.path, 'file'));
// create a file
await file.create(recursive: true);
await file.writeAsString('Hello world!');
// read a file
print('file: ${await file.readAsString()}');
// use a file link if supported
if (fs.supportsFileLink) {
final link = Link(join(dir.path, 'link'));
await link.create(basename(file.path));
final linkFile = File(link.path);
print('link: ${await linkFile.readAsString()}');
}
// list dir content
print(await dir.list(recursive: true, followLinks: true).toList());
}
Browser usage #
You can simply replace in the in-memory example:
final fs = fileSystemMemory;
by
import 'package:fs_shim/fs_browser.dart';
final fs = fileSystemWeb;
The default implementation on the browser uses fileSystemWeb (backed by
indexedDB through idb_shim).
OPFS usage (Origin Private File System) #
fs_shim also provides an implementation on top of the
browser Origin Private File System
(OPFS). It is a thin layer over the native navigator.storage.getDirectory()
API (using dart:js_interop, so it is Wasm compatible).
import 'package:fs_shim/fs_opfs_web.dart';
final fs = fileSystemOpfsWeb;
There is a single OPFS per origin, so fileSystemOpfsWeb returns a shared
instance. The same File/Directory code shown in the examples above runs
unchanged on OPFS.
When to use OPFS instead of fileSystemWeb:
- OPFS is backed directly by the browser's native file system primitives, which is well suited for storing larger files and binary content.
fileSystemWeb(indexedDB) supports links and (opt-in) random access, which OPFS does not.
Limitations of the OPFS implementation:
- Links are not supported (
fs.supportsLink/fs.supportsFileLinkreturnfalse). - Random access is not supported (
fs.supportsRandomAccessreturnsfalse). - Web only โ
fileSystemOpfsWebrelies onnavigator.storage.getDirectory(). The import is safe on any platform, but accessing the instance off the web is not supported.
Instead of the origin private root, a file system can also be rooted at any JS
FileSystemDirectoryHandle (File System Access API), for example a directory
picked by the user:
import 'package:fs_shim/fs_opfs_web.dart';
import 'package:web/web.dart';
final handle = await window.showDirectoryPicker().toDart; // Chromium-only
final fs = fileSystemOpfsWebWithRootHandle(handle);
The handle is used as-is (any interop binding works). Write operations require
the handle to have been granted readwrite permission, and handles do not
persist across page reloads unless saved by the application (e.g. in indexedDB)
and permission requested again.
Random access support #
Random access is supported since version 2.1.0 on io and web (indexedDB).
The default web implementation is not optimized for random access (it might change in the future). You can specify a paging parameter (initial testing is good in some scenarios with a 16Kb page; you might tune it for your needs).
import 'package:fs_shim/fs_browser.dart';
// Use default paging 16Kb
final fs =
fileSystemWeb.withIdbOptions(options: FileSystemIdbOptions.pageDefault);
Storage remains compatible if the options are changed.
Random access is not supported by the OPFS implementation.
Utilities #
- Lightweight glob support (
**,*and?in a posix style path) - Copy utilities (copy files, directories recursively)
Testing #
fs_shim is well suited for testing file system access in VM unit tests using
fileSystemMemory, then running the exact same code on io or the web.
Dev dependencies #
Stable
fs_shim: any
Bleeding edge
fs_shim:
git: https://github.com/tekartik/fs_shim.dart
Features and bugs #
-
On Windows file links are not supported (
fs.supportsFileLinkreturnsfalse) -
On Windows directory link targets are absolute
-
On the web (indexedDB), the size of the file system is limited by the size limit of indexedDB databases (browser dependent)
-
On the web (OPFS), links and random access are not supported
-
Project source code