Interacting with the Filesystem
The @wasmer/sdk package provides users with the ability to mount directories
inside a running WASIX instance. This allows both the WASIX instance and a
JavaScript user to communicate via a shared folder.
Let's say you're using the FFmpeg WebAssembly module to convert a video file
from one format to another. You will need to store your input file somewhere so
that the WASIX program can use it. This is where a shared Directory comes in.
You'll store your input file in the shared directory, and then the WASIX program
will be able to read it and process it. When the processing is done, the output
file can be written back to the shared directory so the JavaScript host can
access it.
Instantiating a Directory
import { Directory } from "@wasmer/sdk";
const dir = new Directory();The Directory class is used to create a directory that can be mounted inside a
WASIX instance's file system. You can create as many Directory instances as
you want.
Adding Files to a Directory
The writeFile() method creates a new file in the specified directory, setting
its content to the provided data. The first argument is the path of the file,
and the second argument is the content of the file.
import { Directory } from "@wasmer/sdk";
const dir = new Directory();
await dir.writeFile("/file.txt", "Hello, World!");The content of the file may be a string or Uint8Array.
Creating Sub-Directories
The createDir method is used to create a directory in the virtual file system.
The argument is the path of the directory.
import { Directory } from "@wasmer/sdk";
const dir = new Directory();
await dir.createDir("/pictures");Now you can place files in the directory you just created like this:
import { Directory } from "@wasmer/sdk";
// reading an image file as a blob and converting it to an Uint8Array
const response = await fetch("https://example.com/image.png");
const blob = await response.blob();
const image = await blob.arrayBuffer();
const dir = new Directory();
await dir.createDir("/pictures");
await dir.writeFile("/pictures/image.png", new Uint8Array(image));Reading Files from a Directory
There are two ways to read files from a directory.
1. The readFile Method
The readFile() method will read the file's bytes back as a Uint8Array.
import { Directory } from "@wasmer/sdk";
const dir = new Directory();
const bytes = await dir.readFile("/file.bin");2. The readTextFile Method
The readTextFile() method will read a file from the Directory and parse it as
a UTF-8 string. The argument is the path of the file.
import { Directory } from "@wasmer/sdk";
const dir = new Directory();
const text = await dir.readTextFile("/output.txt");Listing a Directory's Contents
The readDir() method is analogous to the ls command in Linux. The argument
is the path of the directory.
import { Directory } from "@wasmer/sdk";
const dir = new Directory();
await dir.createDir("/mydir");
await dir.writeFile("/mydir/file.txt", new Uint8Array());
await dir.createDir("/mydir/pictures");
await dir.writeFile("/mydir/pictures/image.png", new Uint8Array());
await dir.createDir("/mydir/videos");
await dir.writeFile("/mydir/videos/video.mp4", new Uint8Array());
const entries = await dir.readDir("/mydir");
console.log(entries);The output of the above code will be:
[
{
"name": "file.txt",
"type": "file"
},
{
"name": "pictures",
"type": "directory"
},
{
"name": "videos",
"type": "directory"
}
]Mounting a Directory
The primary way to use Directory is by mounting it in a Wasmer package or
WASIX instance.
Both Command.run() and runWasix() accept a mount option which accepts
a mapping from paths to the Directory that should be mounted to that path.
For example, this is how you can mount a Directory to /app and use the
cat command from sharrattj/coreutils to print its contents.
import { Wasmer, Directory } from "@wasmer/sdk";
const dir = new Directory();
await dir.writeFile("/file.txt", "Hello, World!");
const pkg = await Wasmer.fromRegistry("sharrattj/coreutils");
const cat = pkg.commands["cat"];
const instance = await cat.run({
args: ["/app/file.txt"],
mount: { "/app": dir },
});
let output = await instance.wait();
console.log(output.stdout); // Hello, World!The same can also be done for a WebAssembly module instantiated from the user instead of the registry.
import { runWasix, Directory } from "@wasmer/sdk";
const python = await WebAssembly.compile(fs.readFileSync("./python.wasm"));
const instance = await runWasix(python, {
program: "python",
args: ["/app/main.py"],
mount: {
"/app": {
"main.py": "print('Hello, World!')",
},
},
});DirectoryInit Shorthand
As a shortcut for initializing a Directory when all contents are known
up-front, you may provide the new Directory() constructor with a mapping from
file paths to their contents. The TypeScript type definitions use the
DirectoryInit type alias to represent this mapping.
import { Directory } from "@wasmer/sdk";
const dir = new Directory({
"/file.txt": "Hello, World!",
"/path/to/nested/file.bin": new Uint8Array(),
});As well as a Directory instance, both Command.run() and runWasix() accept
the DirectoryInit shorthand when specifying mounts.
This is useful when you don't need to hang on to the Directory handle or
access the Directory while the WASIX instance is running.
Removing Files and Directories from a Directory
The removeFile method is used to remove a file from the Directory. The
argument is the path of the file.
The removeDir method is used to remove a directory from the Directory. The
argument is the path of the directory.
import { Directory } from "@wasmer/sdk";
const dir = new Directory();
await dir.createDir("/mydir");
await dir.writeFile("/mydir/file.txt", new Uint8Array());
await dir.removeFile("/mydir/file.txt");
await dir.removeDir("/mydir");The removeDir method will only remove empty directories. If you want to
remove a directory that has files in it, then you'll have to remove the files
first. Its behavior is analogous to the rmdir command in Linux.
Kitchen Sink
Here is a comprehensive example of the Directory API.
It includes:
- Creating an empty
Directory - Mounting directories inside the newly spawned
pythoninstance - Using the
DirectoryInitshorthand to simplify passing in the/src/main.py - Reading back the generated
/out/version.txt
import { init, Directory, Wasmer } from "@wasmer/sdk";
// Initialize the Wasmer SDK
await init();
// Download the Python package
const python = await Wasmer.fromRegistry("python/python");
// The script to be executed
const script = `
import sys
with open("/out/version.txt", "w") as f:
f.write(sys.version)
`;
// A shared directory where the output will be written
const out = new Directory();
// Running the Python script
const instance = await python.entrypoint!.run({
args: ["/src/main.py"],
mount: {
"/out": out,
"/src": {
"main.py": script,
}
},
});
const output = await instance.wait();
if (!output.ok) {
throw new Error(`Python failed with exit code ${output.code}: ${output.stderr}`);
}
// Read the version string back
const pythonVersion = await out.readTextFile("/version.txt");
console.log(pythonVersion) // 3.11.6 (main, Oct 2 2023, 13:45:54) [Clang 15.0.0 (clang-1500.0.40.1)]