使用浏览器文件选择器对话框在 Emscripten 中打开文件

Open a file in Emscripten using browser file selector dialogue

我正在编写一个 C++ 程序以使用 Emscripten 编译为 Web 程序集,以便在浏览器中 运行。

使用户的浏览器打开文件上传的文件选择器对话框并从 C++ 访问该文件内容的最简单方法是什么?

如果可能的话,我不想向包含 wasm canvas 的页面添加可见元素,理想情况下,该过程应该从 C++ 程序本身触发。

有很多方法可以做到这一点,但到目前为止最干净的(需要最少的代码,并且不需要复杂使用 Emscripten 文件系统 API)如下:

在您的构建系统中,添加 -sEXPORTED_RUNTIME_METHODS=[ccall] 以导出 ccall,允许您从 javascript.

调用 C 函数

在您的 html 脚本部分,添加以下函数 - 这将创建一个 FileReader,它从文件输入读取文件,将其复制到堆中,并将地址和大小传递给您定义的 C 函数:

var open_file = function(e) {
  const file_reader = new FileReader();
  file_reader.onload = (event) => {
    const uint8Arr = new Uint8Array(event.target.result);
    const num_bytes = uint8Arr.length * uint8Arr.BYTES_PER_ELEMENT;
    const data_ptr = Module._malloc(num_bytes);
    const data_on_heap = new Uint8Array(Module.HEAPU8.buffer, data_ptr, num_bytes);
    data_on_heap.set(uint8Arr);
    const res = Module.ccall('load_file', 'number', ['number', 'number'], [data_on_heap.byteOffset, uint8Arr.length]);
  };
  file_reader.readAsArrayBuffer(e.target.files[0]);
};

在您的 C++ 代码中的某处,定义一个 C 函数来处理文件内容:

extern "C" {

EMSCRIPTEN_KEEPALIVE int load_file(uint8_t *buffer, size_t size) {
  /// Load a file - this function is called from javascript when the file upload is activated
  std::cout << "load_file triggered, buffer " << &buffer << " size " << size << std::endl;

  // do whatever you need with the file contents

  return 1;
}

}
这里需要

EMSCRIPTEN_KEEPALIVEextern "C"; EMSCRIPTEN_KEEPALIVE还增加了导出的功能,不需要手动导出

最后,当您想要弹出打开文件选择器对话框以提示用户选择文件时,请在您的 C++ 代码中插入以下内容:

#include <emscripten.h>

...

EM_ASM(
  var file_selector = document.createElement('input');
  file_selector.setAttribute('type', 'file');
  file_selector.setAttribute('onchange','open_file(event)');
  file_selector.setAttribute('accept','.png,.jpeg'); // optional - limit accepted file types 
  file_selector.click();
);

这将创建一个具有您指定属性的文件选择器输入元素,并立即模拟对其的点击 - 此方法允许您在自己的界面或其他 C++ 逻辑中使用按钮,而不依赖于浏览器呈现输入元素。