The virtual file system
“Where did my output go?” is the most-asked question in the issue tracker. Short answer: into a virtual filesystem you control. This page is the long answer.
The model
In a browser there is no real disk, so GDAL reads and writes a virtual filesystem. You decide which backing store a path uses: /opfs/… persists in the Origin Private File System, /vsimem/… lives in memory, and GDAL's own VSI prefixes (/vsizip/ & co.) layer on top of either. On Node and React Native there's no indirection: paths are host paths.
OPFS: persistent, big-file friendly
const Module = await initCppJs({ useWorker: true, fs: { opfs: true } });
// in: standard web API writes into OPFS
const dir = await navigator.storage.getDirectory();
const fh = await dir.getFileHandle("input.tif", { create: true });
const ws = await fh.createWritable(); await ws.write(file); await ws.close();
// gdal sees it as /opfs/input.tif
const ds = await Gdal.open("/opfs/input.tif");
const out = await ds.translate("/opfs/out.tif", opts);
await out.close();
// out: read the result back with the same web API
const outHandle = await dir.getFileHandle("out.tif");
const blob = await outHandle.getFile(); // → download, upload, display
OPFS survives page reloads and handles multi-gigabyte files. It's the right default for user-supplied data.
In-memory: /vsimem/
const bytes = new Uint8Array(await file.arrayBuffer());
await Gdal.fileFromMemBuffer("/vsimem/in.geojson", bytes);
const ds = await Gdal.open("/vsimem/in.geojson");
// … later
await Gdal.unlink("/vsimem/in.geojson");
Fast and ephemeral, but the whole file occupies wasm memory. Prefer OPFS beyond a few hundred megabytes (see memory).
Zipped data: /vsizip/ and friends
const ds = await Gdal.open("/vsizip//opfs/districts.zip");
GDAL's chained VSI handlers work on any backing path: read inside archives with /vsizip/, and discover what a multi-dataset file contains with Gdal.getSubdatasetInfo(path). Available prefixes on your build: Gdal.getFileSystemsPrefixes().
FS management API
| CALL | PURPOSE |
|---|---|
Gdal.readDir(dir) / readDirRecursive(dir) | List entries |
Gdal.mkdir(dir, mode) / mkdirRecursive(dir, mode) | Create directories (or Module.FS.mkdirTree(dir)) |
Gdal.unlink(path) / unlinkBatch(paths) | Delete files |
Gdal.rmdir(dir) / rmdirRecursive(dir) | Delete directories |
Gdal.rename(from, to) · Gdal.copyFile(to, from) | Move / copy |
Gdal.fileFromMemBuffer(path, bytes) | Create a /vsimem file from bytes |
ds.getFileList() | All files belonging to a dataset (Shapefile = .shp + .shx + .dbf …) |
Module.getFileList(dir) | Outputs in a directory, with sizes |
ds.getFileList() instead of assuming a single path: the v2 era's issue #45, solved structurally.
Network sources: not yet
/vsicurl/, /vsis3/ and WMS/WFS need an HTTP client inside the wasm build; that work is tracked in issue #67. Today: fetch() the bytes yourself and hand them to GDAL via OPFS or /vsimem/.