WebAssembly(WASM)虛擬文件系統(FS)是一個用于在WebAssembly環境中模擬文件系統的工具。它允許開發者在瀏覽器或其他WebAssembly運行環境中使用類似于本地文件系統的功能。Emscripten提供了一個虛擬文件系統,以兼容libc/libcxx的同步文件訪問函數。

Emscripten 虛擬文件系統架構

Emscripten 虛擬文件系統架構包括三種主要的文件系統:

  • MEMFS:MEMFS 內存文件系統,數據存儲于內存中,頁面刷新或程序重載后數據丟失
  • NODEFS:Node.js 文件系統,可以訪問本地文件系統,適用于 Node.js 環境
  • IDBFS:IndexedDB 文件系統,基于瀏覽器的IndexedDB對象,可以持久化存儲

這些文件系統通過 JavaScript 對象FS封裝,供 fopen()、fread()、fwrite() 等 libc/libcxx 文件訪問函數調用。

MEMFS

在使用MEMFS之前,需要將文件打包。可以使用emcc命令行或file_packager.py工具進行打包。以下是一個簡單的示例:

// packfile.cc
int main() {
    FILE* fp = fopen("hello.txt", "rt");
    if (fp) {
        while (!feof(fp)) {
            char c = fgetc(fp);
            if (c != EOF) {
                putchar(c);
            }
        }
        fclose(fp);
    }
    return 0;
}

使用以下命令打包文件:

emcc packfile.cc -o packfile.js --preload-file hello.txt

NODEFS示例

NODEFS允許訪問本地文件系統,以下是一個使用NODEFS的示例:

// nodefs.cc
void setup_nodefs() {
    EM_ASM(
        FS.mkdir('/data');
        FS.mount(NODEFS, {root:'.'}, '/data');
    );
}

int main() {
    setup_nodefs();
    FILE* fp = fopen("/data/nodefs_data.txt", "r+t");
    if (fp == NULL) fp = fopen("/data/nodefs_data.txt", "w+t");
    int count = 0;
    if (fp) {
        fscanf(fp, "%d", &count);
        count++;
        fseek(fp, 0, SEEK_SET);
        fprintf(fp, "%d", count);
        fclose(fp);
        printf("count:%d\n", count);
    } else {
        printf("fopen failed.\n");
    }
    return 0;
}

使用以下命令編譯代碼:

emcc nodefs.cc -o nodefs.js

IDBFS示例

IDBFS基于IndexedDB,可以持久化存儲數據。以下是一個使用IDBFS的示例:

void sync_idbfs() {
    EM_ASM(
        FS.syncfs(function (err) {});
    );
}

EM_PORT_API(void) test() {
    FILE* fp = fopen("/data/nodefs_data.txt", "r+t");
    if (fp == NULL) fp = fopen("/data/nodefs_data.txt", "w+t");
    int count = 0;
    if (fp) {
        fscanf(fp, "%d", &count);
        count++;
        fseek(fp, 0, SEEK_SET);
        fprintf(fp, "%d", count);
        fclose(fp);
        printf("count:%d\n", count);
        sync_idbfs();
    } else {
        printf("fopen failed.\n");
    }
}

int main() {
    EM_ASM(
        FS.mkdir('/data');
        FS.mount(IDBFS, {}, '/data');
        FS.syncfs(true, function (err) {
            assert(!err);
            ccall('test', 'v');
        });
    );
    return 0;
}