You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Emscripten编译C++到WASM时如何从C++侧触发浏览器文件选择器读取文件

最简实现方案

这个方案不需要修改宿主HTML添加可见元素,所有逻辑可以封装在C++代码中,编译后自动注入的DOM元素默认隐藏,不会对页面展示产生影响。

核心思路

  • 用Emscripten的EM_JS宏内嵌JS代码,动态创建隐藏的<input type="file">元素
  • C++侧触发JS代码调用input的点击事件弹出文件选择框
  • 文件选中后用JS读取文件内容,通过Emscripten的内存接口把内容传回C++侧

完整实现代码

#include <emscripten.h>
#include <iostream>
#include <functional>
#include <string>

// 存储文件读取完成的回调
std::function<void(const std::string&)> file_read_callback;

// 暴露给JS调用的C接口,用于接收文件内容
extern "C" {
    void on_file_selected(const char* content, int length) {
        if (file_read_callback) {
            file_read_callback(std::string(content, length));
        }
    }
}

// 触发文件选择的C++接口
void open_file_dialog(std::function<void(const std::string&)> callback) {
    file_read_callback = std::move(callback);
    // 调用内嵌JS代码弹出选择框
    EM_JS(void, trigger_file_picker, (), {
        // 全局只创建一次隐藏的input元素
        if (!window._hiddenFileInput) {
            const input = document.createElement('input');
            input.type = 'file';
            input.style.display = 'none';
            // 如需限制文件类型可添加下一行,按需修改接受的后缀
            // input.accept = '.txt,.cpp,.json';
            input.addEventListener('change', (e) => {
                const file = e.target.files[0];
                if (!file) return;
                const reader = new FileReader();
                reader.onload = (evt) => {
                    const content = new Uint8Array(evt.target.result);
                    // 分配WASM内存空间
                    const ptr = _malloc(content.length);
                    // 把文件内容复制到WASM内存
                    HEAPU8.set(content, ptr);
                    // 调用C++回调传递内容
                    _on_file_selected(ptr, content.length);
                    // 释放内存
                    _free(ptr);
                    // 重置input,允许重复选择同一个文件
                    input.value = '';
                };
                reader.readAsArrayBuffer(file);
            });
            document.body.appendChild(input);
            window._hiddenFileInput = input;
        }
        window._hiddenFileInput.click();
    });
    trigger_file_picker();
}

// 测试示例
int main() {
    std::cout << "等待用户选择文件..." << std::endl;
    // 注意:open_file_dialog必须绑定到用户交互事件触发(比如鼠标点击、键盘按下),不能直接在main里自动调用,浏览器安全策略会拦截
    open_file_dialog([](const std::string& content) {
        std::cout << "读取文件完成,长度:" << content.size() << std::endl;
        std::cout << "前100字符内容:" << content.substr(0, 100) << std::endl;
    });
    return 0;
}

编译命令

emcc main.cpp -o index.html -s EXPORTED_FUNCTIONS=['_malloc','_free','_on_file_selected'] -s EXPORTED_RUNTIME_METHODS=['ccall','cwrap']

关键注意事项

  • 浏览器安全限制要求文件选择对话框必须由用户交互事件触发(比如鼠标点击、键盘按键),不能在页面加载后自动触发,你需要把open_file_dialog的调用绑定到C++侧捕获的用户交互事件(比如ImGui按钮点击、GLFW鼠标按键事件)上才能正常弹出选择框
  • 如果需要读取二进制文件,仅需把回调的参数类型从std::string改为std::vector<uint8_t>即可,无需修改JS侧逻辑

内容的提问来源于stack exchange,提问作者Riot

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 21:45:08