打包文件

本主题介绍如何打包将在页面加载时用于填充 Emscripten 的虚拟文件系统 的文件。

打包文件有两种方式:预加载嵌入。嵌入将指定的文件存储在 wasm 文件中,而预加载则将它们打包到一个单独的包中。嵌入文件比预加载更有效,因为不需要下载和复制单独的文件,但预加载允许选择单独托管数据。

Emcc 使用文件打包器来打包文件并生成在运行时创建和加载文件系统的 文件系统 API 调用。虽然 Emcc 是打包的推荐工具,但有些情况下手动运行文件打包器可能更合理。

使用 --use-preload-plugins,可以根据文件的扩展名自动解码文件。有关更多信息,请参见 预加载文件

使用 emcc 打包

打包文件最简单的方法是在编译时使用 emccpreloadembed 命令分别选择它们各自的打包方式。

以下命令展示了如何打包用于预加载的文件

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

该命令生成 file.htmlfile.jsfile.data.data 文件包含 asset_dir/ 中的所有文件,并由 file.js 加载。

注意

教程 使用 hello_world_file.cpp 测试代码演示了预加载。

以下命令展示了嵌入的方式。在这种情况下,emcc 生成 file.htmlfile.js——asset_dir/ 的内容直接嵌入到 file.js 中。

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

默认情况下,要打包的文件应嵌套在编译时命令提示符目录下或其下方。在运行时,相同的嵌套文件结构将映射到虚拟文件系统,其中根目录对应于命令提示符目录。

例如,考虑一个文件结构 dir1/dir2/dir3/asset_dir/,其中项目是从 dir2 编译的。当我们打包 asset_dir 时,我们指定其相对位置 dir3/asset_dir/

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

该文件夹在运行时在虚拟文件系统中以相同的路径 dir3/asset_dir 可用。类似地,如果我们打包了 dir2 中的文件,它将在运行时的虚拟文件系统根目录中可用。

@ 符号可用于将打包的文件从本地文件系统中的任何位置映射到虚拟文件系统中的任何位置。这将在下面的 修改虚拟文件系统中的文件位置 中讨论。

使用文件打包器工具打包

您也可以使用 file_packager 开头的说明手动运行文件打包器

文件打包器生成一个 .data 文件和一个 .js 文件。.js 文件包含使用数据文件的代码,必须在加载主编译代码之前加载。(例如,在 --shell-file 的末尾添加 <script> 标记,在 {{{ SCRIPT }}}` 之前。)

注意

  • 使用文件打包器可以让您单独运行文件打包,而不用编译代码。

  • 您可以通过对每个文件运行文件打包器并加载 .js 输出,来加载多个数据文件。参见 BananaBread 获取动态加载的示例(cube2/js/game-setup.js)。

更改数据文件位置

默认情况下,包含所有预加载文件的数据文件 .data 从与您的 .js 文件相同的 URL 加载。在某些情况下,将数据文件存储在与其他文件不同的位置可能很有用——例如,如果您的 .html.js 经常更改,您可能希望将数据文件保留在其他地方的快速 CDN 上。

可以通过指定 Module.locateFile 函数来支持此模型,该函数返回数据文件的存储 URL。该函数必须在加载数据文件的 <script> 元素之前指定。

修改虚拟文件系统中的文件位置

打包的默认方法是直接将编译时嵌套文件结构(相对于编译时命令提示符目录)映射到虚拟文件系统的根目录。在构建时,路径中的 @ 符号可用于显式指定资源在运行时将位于虚拟文件系统中的哪个位置。

注意

需要 @ 符号是因为有时需要打包嵌套在编译时目录下的文件,因此没有默认映射到虚拟文件系统中的位置。

例如,我们可以使用以下命令将预加载文件夹 ../../asset_dir 映射到虚拟文件系统的根目录 (/)

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

我们还可以映射新的路径和文件名。例如,要使嵌入文件 ../res/gen123.png 作为 /main.png 可用,我们可以执行以下操作

emcc file.cpp -o file.html --embed-file ../res/[email protected]

有效字符集

文件名可以使用以下字符:A-Za-z0-9、空格字符以及任何字符 !#$%&'()+,-.;=@[]^_`{}~。此外,如果您的主机文件系统支持,以下字符也可以使用:"*<>?|(Windows 不允许在文件名中使用这些字符)。在命令行上指定字符 @ 时,必须将其转义为 @@ 的形式,以避免触发 src@dst 映射表示法(见上文)。/\: 字符不可使用。

监控文件使用情况

重要

只打包您的应用程序真正需要的文件,以减少下载大小并提高启动速度。

有一个选项可以记录运行时实际使用的文件。要使用它,请定义 Module.logReadFiles 对象。读取的每个文件都将记录到 stderr 中。

另一种方法是查看编译后的 JavaScript 中的 FS.readFiles()。这是一个包含所有读取文件的键的对象。您可能会发现它比记录更容易使用,因为它记录的是文件,而不是潜在的多个文件访问。

注意

您还可以修改 FS.readFiles() 对象或将其完全删除。这很有用,例如,要查看应用程序中两个时间点之间读取了哪些文件。

预加载文件

使用 --use-preload-plugins,可以根据文件的扩展名自动解码文件。也可以通过对每个文件调用 emscripten_run_preload_plugins() 手动完成此操作。这些文件以其原始形式存储在文件系统中,但它们的解码形式可以直接使用。

支持以下格式

  • 图像 (.jpg, .jpeg, .png, .bmp): 这些文件使用浏览器的图像解码器解码,然后可以通过 IMG_Load 使用(SDL1 和 SDL2 端口,依赖于 emscripten_get_preloaded_image_data())。(将 Module.noImageDecoding 设置为 true 以禁用)。

  • 音频 (.ogg, .wav, .mp3): 这些文件使用浏览器的音频解码器解码,然后可以通过 Mix_LoadWAV 使用(仅限 SDL1)。(将 Module.noAudioDecoding 设置为 true 以禁用)。

  • 动态库 (.so): 这些文件使用 WebAssembly.instantiate 预编译和实例化。这对于需要异步编译大型 WebAssembly 模块的浏览器(如 Chrome)很有用,如果您随后希望使用 dlopen 异步加载模块。(将 Module.noWasmDecoding 设置为 true 以禁用)。

测试代码

测试套件 包含许多文件打包示例,是搜索工作代码的好地方。