Emscripten代码移植之文件和文件系统(四)

翻译:云荒杯倾

这部分是关于如何在 Emscripten编译的代码中使用文件。包括以下部分:

  • 文件系统概览:总体介绍Emscripten支持的文件操作。
  • 打包文件:怎样使用emcc来打包编译后的代码所需要的文件。
  • 同步虚拟XHR后台文件系统使用:介绍如何使用XHR通过http完成二进制数据的懒加载。

下面逐一讲这三部分。

1、文件系统概述


分两部分,一部分介绍Emscripten的文件系统运行环境,一部分介绍Emscripten 文件系统体系架构。

Emscripten的文件系统运行环境

原生代码和JS使用的文件存取模式有很大的不同。原生代码使用libc和libcxx库调用同步文件APIs,而JS中除了web worker都是只允许异步文件获取的。另外,因为JS处在浏览器沙箱环境中,它并不是直接对主机的文件系统进行存取操作的。

Emscripten提供了一个虚拟的文件系统模拟本地文件系统,所以原生代码可以可以在很少或者不需要修改的情况下使用同步文件APIs。

打包文件可以让你指定你需要的文件,通过emcc将他们打包进虚拟文件系统。对于开发者来说,打包文件这一部分的知识是需要了解的。

Emscripten文件系统体系架构

下面列出了Emscripten文件系统的主要元素。大多数原生代码都是调用libc和libcxx库的同步文件API的,接下来他们将依次调用底层的文件系统API,默认使用MEMFS虚拟文件系统。

文件系统体系结构

当运行时初始化时,MEMFS挂载在根目录下。而添加到MEMFS的文件是在编译期间通过emcc打包进来的。当HTML页面加载完成后,JS使用同步XHR异步加载(load)这些文件。只有当异步加载完成,文件在虚拟文件系统可用的时候,才能执行编译代码

因为MEMFS实际上存在内存中,页面reload完成的时候,所有写入的数据都会丢失掉。如果想持久化这些写入的东西,请在浏览器中挂载IDBFS文件系统,或者Nodejs中挂载NODEFS。NODEFS可以访问本地文件系统。你可以通过自己写JS的方式挂载新的文件系统,然后执行这些文件系统中的适合你的文件操作。

如果你需要从网络取其他文件到文件系统,请使用 Emscripten Asynchronous File System API中的emscripten_wget()和其他方法。这些方法是同步的,应用程序必须等待回调完成完成。

2、打包文件


预加载(preloading)嵌入(embedding)两种可交换的打包方式。嵌入式是将具体文件与编译生生的JS文件混到一起,放同一个文件。而预加载可以将文件单独打包到一个文件中。嵌入的方式比预加载低效,使用情况是要打包的文件数量和文件大小都比较小。

emcc使用文件打包器打包文件,然后在创建和加载文件系统的时候生成文件系统调用。虽然emcc是推荐的打包工具,但是你也可以直接手动使用文件打包器进行自行打包。

用emcc打包

打包文件最简单的方式是在编译的时候通过emcc打包。preload和embed代表你选择的打包方式。

下面是一个示例打包命令:

1
./emcc file.cpp -o file.html --preload-file asset_dir

这个命令会生成file.html,file.js,file.data。这个.data文件包含了asset_dir目录下的所有文件,通过file.js来加载它。

下面这个命令展示了嵌入方式打包。此时,emcc生成file.html,file.js。 asset_dir/ 目录中的内容都直接被打包进file.js了。

1
./emcc file.cpp -o file.html --embed-file asset_dir

默认下,要打包的文件应该嵌套在或就在编译命令此时cd的目录下。运行时,虚拟文件系统会映射同样的要打包文件的目录结构。虚拟文件系统的根目录对应着编译命令此时cd的目录

举例,有dir1/dir2/dir3/asset_dir/的一个目录结构,dir2是要编译的项目,我们要打包asset_dir的话,就用相对路径dir3/asset_dir/。emcc的命令窗口在dir2目录打开。

1
./emcc file.cpp -o file.html --preload-file dir3/asset_dir

打包编译完成后,在虚拟文件系统中也是通过dir3/asset_dir这样的路径来找asset_dir文件夹。相似的,如果我们是在dir2下打包了一个文件的话,项目运行时就应该在虚拟文件系统的根目录下来找dir2目录。

打包时,还有一个@符号的用法,它是指将任何本地文件系统中任何目录下的文件映射到编译后的虚拟文件系统中的某路径。

用文件打包器打包

除了通过emcc命令编译时打包,你也可以手动运行文件打包器file_packager.py来打包。

打包器会成.data文化和.js文件。.js文件包含使用.data文件的代码。

1
2
3
NOTE:
* 使用文件打包器打包使你不用非要在编译的时候(用emccd来编译)打包。
* 使用文件打包器可对多个数据文件逐一打包,然后输出很多.js文件。

改变.data文件的位置

默认下,.data文件和.js文件通过相同URL加载。有时候让你的数据文件和其他类型文件分开,处在不同位置是有用的。

使用 Module.filePackagePrefixURL完成存储前的路径修改。这个属性要在加载它的