File operations in Emscripten are provided by the FS library. It is used internally for all of Emscripten’s libc and libcxx file I/O.
注解
The API is inspired by the Linux/POSIX File System API, with each presenting a very similar interface.
The underlying behaviour is also similar, except where differences between the
native and browser environments make this unreasonable. For example, user and
group permissions are defined but ignored in FS.open()
.
Emscripten predominantly compiles code that uses synchronous file I/O, so the majority of the FS
member functions offer a synchronous interface (with errors being reported by raising exceptions of type FS.ErrnoError).
File data in Emscripten is partitioned by mounted file systems. Several file systems are provided. An instance of MEMFS is mounted to /
by default. The subdirectories /home/web_user and /tmp are also created automatically, in addition to several other special devices and streams (e.g. /dev/null, /dev/random, /dev/stdin, /proc/self/fd); see FS.staticInit() in the FS library for full details. Instances of NODEFS and IDBFS can be mounted to other directories if your application needs to persist data.
The automatic tests in tests/test_core.py (search for test_files
) contain many examples of how to use this API. The tutorial also shows how to pre-load a file so that it can be read from compiled C/C++.
A high level overview of the way File Systems work in Emscripten-ported code is provided in the 文件系统概述.
Emscripten decides whether to include file system support automatically. Many programs don’t need files, and file system support is not negligible in size, so Emscripten avoids including it when it doesn’t see a reason to. That means that if your C/C++ code does not access files, then the FS
object and other file system APIs will not be included in the output. And, on the other hand, if your C/C++ code does use files, then file system support will be automatically included. So normally things will “just work” and you don’t need to think about this at all.
However, if your C/C++ code doesn’t use files, but you want to use them from JavaScript, then you can build with -s FORCE_FILESYSTEM=1
, which will make the compiler include file system support even though it doesn’t see it being used.
On the other hand, if you want to not include any filesystem support code (which may be included even due to printf or iostreams, due to how musl and libc++ are structured), you can build with -s FILESYSTEM=0
. Very simple stdout support will be included if necessary in such a case, enough for printf and such to work, but no filesystem code will be added, which can save a significant amount of code size.
Applications compiled with Emscripten usually expect synchronous I/O, so Emscripten itself provides file systems with completely synchronous interfaces.
However, due to JavaScript’s event-driven nature, most persistent storage options offer only asynchronous interfaces. Emscripten offers multiple file systems that can be mounted with FS.mount()
to help deal with persistence depending on the execution context.
注解
Only the MEMFS filesystem is included by default. All others must be enabled explicitly, using -lnodefs.js
(NODEFS), -lidbfs.js
(IDBFS), -lworkerfs.js
(WORKERFS), or -lproxyfs.js
(PROXYFS).
This is the default file system mounted at /
when the runtime is initialized. All files exist strictly in-memory, and any data written to them is lost when the page is reloaded.
注解
This file system is only for use when running inside node.js.
This file system lets a program in node map directories (via a mount operation) on the host filesystem to directories in Emscripten’s virtual filesystem. It uses node’s synchronous FS API to immediately persist any data written to the Emscripten file system to your local disk.
See this test for an example.
注解
This file system is only for use when running code inside a browser.
The IDBFS file system implements the FS.syncfs()
interface, which when called will persist any operations to an IndexedDB
instance.
This is provided to overcome the limitation that browsers do not offer synchronous APIs for persistent storage, and so (by default) all writes exist only temporarily in-memory.
注解
This file system is only for use when running code inside a worker.
This file system provides read-only access to File
and Blob
objects inside a worker without copying the entire data into memory and can potentially be used for huge files.
Emscripten supports registering arbitrary device drivers composed of a device id and a set of device-specific stream callbacks. Once a driver has been registered with FS.registerDevice()
, a device node can be created to reference it (using FS.mkdev()
).
The device node acts as an interface between the device and the file system. Any stream referencing the new node will inherit the stream callbacks registered for the device, making all of the high-level FS operations transparently interact with the device.
注解
Every device is different and unique. While common file operations like open
, close
, read
, and write
are typically supported (and inherited by file streams to provide a layer of abstraction for the equivalent libc functions to call), each device should implement whatever callbacks it needs based on its unique characteristics.
FS.
makedev
(ma, mi)¶Converts a major and minor number into a single unique integer. This is used as an id to represent the device.
参数: |
|
---|
FS.
registerDevice
(dev, ops)¶Registers the specified device driver with a set of callbacks.
参数: |
|
---|
Emscripten standard I/O works by going though the virtual /dev/stdin
, /dev/stdout
and /dev/stderr
devices. You can set them up using your own I/O functions by calling FS.init()
.
By default:
stdin
will read from the terminal in command line engines and use window.prompt()
in browsers (in both cases, with line buffering).stdout
will use a print
function if one such is defined, printing to the terminal in command line engines and to the browser console in browsers that have a console (again, line-buffered).stderr
will use the same output function as stdout
.注解
All the configuration should be done before the main run()
method is executed, typically by implementing Module.preRun
. See Interacting with code for more information.
FS.
init
(input, output, error)¶Sets up standard I/O devices for stdin
, stdout
, and stderr
.
The devices are set up using the following (optional) callbacks. If any of the callbacks throw an exception, it will be caught and handled as if the device malfunctioned.
参数: |
|
---|
FS.
mount
(type, opts, mountpoint)¶Mounts the FS object specified by type
to the directory specified by mountpoint
. The opts
object is specific to each file system type.
参数: |
|
---|
FS.
unmount
(mountpoint)¶Unmounts the specified mountpoint
.
参数: |
|
---|
FS.
syncfs
(populate, callback)¶Responsible for iterating and synchronizing all mounted file systems in an asynchronous fashion.
注解
Currently, only the IDBFS file system implements the interfaces needed for synchronization. All other file systems are completely synchronous and don’t require synchronization.
The populate
flag is used to control the intended direction of the
underlying synchronization between Emscripten`s internal data, and the file
system’s persistent data.
For example:
function myAppStartup(callback) {
FS.mkdir('/data');
FS.mount(IDBFS, {}, '/data');
FS.syncfs(true, function (err) {
// handle callback
});
}
function myAppShutdown(callback) {
FS.syncfs(function (err) {
// handle callback
});
}
A real example of this functionality can be seen in test_idbfs_sync.c.
参数: |
|
---|
FS.
mkdir
(path, mode)¶Creates a new directory node in the file system. For example:
FS.mkdir('/data');
注解
The underlying implementation does not support user or group permissions. The caller is always treated as the owner of the folder, and only permissions relevant to the owner apply.
参数: |
|
---|
FS.
mkdev
(path, mode, dev)¶Creates a new device node in the file system referencing the registered device driver (FS.registerDevice()
) for dev
. For example:
var id = FS.makedev(64, 0);
FS.registerDevice(id, {});
FS.mkdev('/dummy', id);
参数: |
|
---|
FS.
symlink
(oldpath, newpath)¶Creates a symlink node at newpath
linking to oldpath
. For example:
FS.writeFile('file', 'foobar');
FS.symlink('file', 'link');
参数: |
|
---|
FS.
rename
(oldpath, newpath)¶Renames the node at oldpath
to newpath
. For example:
FS.writeFile('file', 'foobar');
FS.rename('file', 'newfile');
参数: |
|
---|
FS.
rmdir
(path)¶Removes an empty directory located at path
.
Example
FS.mkdir('data');
FS.rmdir('data');
参数: |
|
---|
FS.
unlink
(path)¶Unlinks the node at path
.
This removes a name from the file system. If that name was the last link to a file (and no processes have the file open) the file is deleted.
For example:
FS.writeFile('/foobar.txt', 'Hello, world');
FS.unlink('/foobar.txt');
参数: |
|
---|
FS.
readlink
(path)¶Gets the string value stored in the symbolic link at path
. For example:
#include <stdio.h>
#include <emscripten.h>
int main() {
MAIN_THREAD_EM_ASM(
FS.writeFile('file', 'foobar');
FS.symlink('file', 'link');
console.log(FS.readlink('link'));
);
return 0;
}
outputs:
file
参数: |
|
---|---|
返回: | The string value stored in the symbolic link at |
FS.
stat
(path)¶Gets a JavaScript object containing statistics about the node at path
. For example:
#include <stdio.h>
#include <emscripten.h>
int main() {
MAIN_THREAD_EM_ASM(
FS.writeFile('file', 'foobar');
console.log(FS.stat('file'));
);
return 0;
}
outputs:
{
dev: 1,
ino: 13,
mode: 33206,
nlink: 1,
uid: 0,
gid: 0,
rdev: 0,
size: 6,
atime: Mon Nov 25 2013 00:37:27 GMT-0800 (PST),
mtime: Mon Nov 25 2013 00:37:27 GMT-0800 (PST),
ctime: Mon Nov 25 2013 00:37:27 GMT-0800 (PST),
blksize: 4096,
blocks: 1
}
参数: |
|
---|
FS.
lstat
(path)¶Identical to FS.stat()
, However, if path
is a symbolic link then the returned stats will be for the link itself, not the file that it links to.
参数: |
|
---|
FS.
chmod
(path, mode)¶Change the mode flags for path
to mode
.
注解
The underlying implementation does not support user or group permissions. The caller is always treated as the owner of the folder, and only permissions relevant to the owner apply.
For example:
FS.writeFile('forbidden', 'can\'t touch this');
FS.chmod('forbidden', 0000);
参数: |
|
---|
FS.
lchmod
(path, mode)¶Identical to FS.chmod()
. However, if path
is a symbolic link then the mode will be set on the link itself, not the file that it links to.
参数: |
|
---|
FS.
fchmod
(fd, mode)¶Identical to FS.chmod()
. However, a raw file descriptor is supplied as fd
.
参数: |
|
---|
FS.
chown
(path, uid, gid)¶Change the ownership of the specified file to the given user or group id.
注解
This call exists to provide a more “complete” API mapping for ported code. Values set are effectively ignored.
参数: |
|
---|
FS.
lchown
(path, uid, gid)¶Identical to FS.chown()
. However, if path
is a symbolic link then the properties will be set on the link itself, not the file that it links to.
注解
This call exists to provide a more “complete” API mapping for ported code. Values set are effectively ignored.
参数: |
|
---|
FS.
fchown
(fd, uid, gid)¶Identical to FS.chown()
. However, a raw file descriptor is supplied as fd
.
注解
This call exists to provide a more “complete” API mapping for ported code. Values set are effectively ignored.
参数: |
|
---|
FS.
truncate
(path, len)¶Truncates a file to the specified length. For example:
#include <stdio.h>
#include <emscripten.h>
int main() {
MAIN_THREAD_EM_ASM(
FS.writeFile('file', 'foobar');
FS.truncate('file', 3);
console.log(FS.readFile('file', { encoding: 'utf8' }));
);
return 0;
}
outputs:
foo
参数: |
|
---|
FS.
ftruncate
(fd, len)¶Truncates the file identified by the fd
to the specified length (len
).
参数: |
|
---|
FS.
utime
(path, atime, mtime)¶Change the timestamps of the file located at path
. The times passed to the arguments are in milliseconds since January 1, 1970 (midnight UTC/GMT).
Note that in the current implementation the stored timestamp is a single value, the maximum of atime
and mtime
.
参数: |
|
---|
FS.
open
(path, flags[, mode])¶Opens a file with the specified flags. flags
can be:
r
— Open file for reading.r+
— Open file for reading and writing.w
— Open file for writing.wx
— Like w
but fails if path exists.w+
— Open file for reading and writing. The file is created if it does not exist or truncated if it exists.wx+
— Like w+
but fails if path exists.a
— Open file for appending. The file is created if it does not exist.ax
— Like a
but fails if path exists.a+
— Open file for reading and appending. The file is created if it does not exist.ax+
— Like a+
but fails if path exists.注解
The underlying implementation does not support user or group permissions. The file permissions set in mode
are only used if the file is created. The caller is always treated as the owner of the file, and only those permissions apply.
参数: |
|
---|---|
返回: | A stream object. |
FS.
close
(stream)¶Closes the file stream.
参数: |
|
---|
FS.
llseek
(stream, offset, whence)¶Repositions the offset of the stream offset
bytes relative to the beginning, current position, or end of the file, depending on the whence
parameter.
The _llseek()
function repositions the offset
of the open file associated with the file descriptor fd
to (offset_high<<32) | offset_low
bytes relative to the beginning of the file, the current position in the file, or the end of the file, depending on whether whence is SEEK_SET
, SEEK_CUR
, or SEEK_END
, respectively. It returns the resulting file position in the argument result.
参数: |
|
---|
FS.
read
(stream, buffer, offset, length[, position])¶Read length
bytes from the stream, storing them into buffer
starting at offset
.
By default, reading starts from the stream’s current offset, however, a specific offset can be specified with the position
argument. For example:
var stream = FS.open('abinaryfile', 'r');
var buf = new Uint8Array(4);
FS.read(stream, buf, 0, 4, 0);
FS.close(stream);
参数: |
|
---|
FS.
write
(stream, buffer, offset, length[, position])¶Writes length
bytes from buffer
, starting at offset
.
By default, writing starts from the stream’s current offset, however, a specific offset can be specified with the position
argument. For example:
var data = new Uint8Array(32);
var stream = FS.open('dummy', 'w+');
FS.write(stream, data, 0, data.length, 0);
FS.close(stream);
参数: |
|
---|
FS.
readFile
(path, opts)¶Reads the entire file at path
and returns it as a string
(encoding is utf8
), or as a new Uint8Array
buffer (encoding is binary
).
参数: |
|
---|---|
返回: | The file as a |
FS.
writeFile
(path, data, opts)¶Writes the entire contents of data
to the file at path
. For example:
FS.writeFile('file', 'foobar');
var contents = FS.readFile('file', { encoding: 'utf8' });
参数: |
|
---|
FS.
createLazyFile
(parent, name, url, canRead, canWrite)¶Creates a file that will be loaded lazily on first access from a given URL or local file system path, and returns a reference to it.
警告
Firefox and Chrome have recently disabled synchronous binary XHRs, which means this cannot work for JavaScript in regular HTML pages (but it works within Web Workers).
Example
FS.createLazyFile('/', 'foo', 'other/page.htm', true, false);
FS.createLazyFile('/', 'bar', '/get_file.php?name=baz', true, true);
参数: |
|
---|---|
返回: | A reference to the new file. |
FS.
createPreloadedFile
(parent, name, url, canRead, canWrite)¶Preloads a file asynchronously, and uses preload plugins to prepare its content. You should call this in preRun
, run()
will be delayed until all preloaded files are ready. This is how the preload-file option works in emcc when --use-preload-plugins
has been specified (if you use this method by itself, you will need to build the program with that option).
参数: |
|
---|
Emscripten’s file system supports regular files, directories, symlinks, character devices, block devices and sockets. Similarly to most Unix systems, all of these file types can be operated on using the higher-level FS operations like FS.read()
and FS.write()
.
FS.
isFile
(mode)¶Tests if the mode
bitmask represents a file.
参数: |
|
---|---|
返回: |
|
返回类型: | bool |
FS.
isDir
(mode)¶Tests if the mode
bitmask represents a directory.
返回: | true if the mode bitmask represents a directory. |
---|---|
返回类型: | bool |
FS.
isLink
(mode)¶Tests if the mode
bitmask represents a symlink.
参数: |
|
---|---|
返回: |
|
返回类型: | bool |
FS.
isChrdev
(mode)¶Tests if the mode
bitmask represents a character device.
参数: |
|
---|---|
返回: |
|
返回类型: | bool |
FS.
isBlkdev
(mode)¶Tests if the mode
bitmask represents a block device.
参数: |
|
---|---|
返回: |
|
返回类型: | bool |
FS.
isSocket
(mode)¶Tests if the mode
bitmask represents a socket.
参数: |
|
---|---|
返回: |
|
返回类型: | bool |
FS.
cwd
()¶Gets the current working directory.
返回: | The current working directory. |
---|
FS.
chdir
(path)¶Sets the current working directory.
参数: |
|
---|
FS.
lookupPath
(path, opts)¶Looks up the incoming path and returns an object containing both the resolved path and node.
The options (opts
) allow you to specify whether the object, its parent component, a symlink, or the item the symlink points to are returned. For example:
var lookup = FS.lookupPath(path, { parent: true });
参数: |
|
---|---|
返回: | an object with the format: {
path: resolved_path,
node: resolved_node
}
|
FS.
analyzePath
(path, dontResolveLastLink)¶Looks up the incoming path and returns an object containing information about
file stats and nodes. Built on top of FS.lookupPath
and provides more
information about given path and its parent. If any error occurs it won’t
throw but returns an error
property.
参数: |
|
---|---|
返回: | an object with the format: {
isRoot: boolean,
exists: boolean,
error: Error,
name: string,
path: resolved_path,
object: resolved_node,
parentExists: boolean,
parentPath: resolved_parent_path,
parentObject: resolved_parent_node
}
|
FS.
getPath
(node)¶Gets the absolute path to node
, accounting for mounts.
参数: |
|
---|---|
返回: | The absolute path to |