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

基于Emscripten异步文件系统加载本地文件遇运行时错误求助

问题分析

你触发的Runtime error:unreachable错误,核心原因是Emscripten的emscripten_async_wget_data API不支持浏览器生成的Blob URL。该API仅能处理两类资源:

  • 标准HTTP/HTTPS远程URL
  • Emscripten虚拟文件系统(FS)内的本地路径

浏览器通过URL.createObjectURL生成的Blob URL是内部临时标识,Emscripten网络层无法解析这类格式,因此触发底层错误。

解决方案

以下提供两种符合你诉求的实现方式,优先推荐方案1(基于虚拟文件系统的路径方案),贴近你希望用URL/路径让Loader模块处理的需求。

方案1:将文件写入Emscripten虚拟文件系统,用虚拟路径加载

步骤1:修改JavaScript侧,将选中文件写入虚拟FS

function onFileOpen(inputData) {
  const file = inputData.files[0];
  const reader = new FileReader();
  
  reader.onload = function(e) {
    // 把文件内容转为Uint8Array
    const arrayBuffer = e.target.result;
    const uint8Array = new Uint8Array(arrayBuffer);
    
    // 定义虚拟文件系统中的存储路径
    const virtualPath = `/tmp/${file.name}`;
    
    // 确保虚拟目录存在并写入文件
    FS.createPath('/', 'tmp', true, true);
    FS.writeFile(virtualPath, uint8Array, { encoding: 'binary' });
    
    // 调用C++方法,传入虚拟路径
    Module.GetLoaderSingleton().LoadAsset(virtualPath, "MyObj", Module.AssetType.Image);
  };
  
  reader.readAsArrayBuffer(file);
}

步骤2:调整C++的Loader::LoadAsset实现

文件已写入虚拟FS,你可以选择继续用异步API(需给路径加file://前缀),或改用更高效的同步读取:

异步API适配版

void Loader::LoadAsset(std::string url, std::string objectName, AssetType type)
{
    // 给虚拟FS路径添加file://前缀,让emscripten_async_wget_data识别
    std::string fsUrl = "file://" + url;
    emscripten_async_wget_data(
        fsUrl.c_str(),
        nullptr,
        onLoadedData,
        onErrorData);
}

同步读取版(更适合本地文件场景)

#include <emscripten/fs.h>

void Loader::LoadAsset(std::string url, std::string objectName, AssetType type)
{
    FILE* fp = fopen(url.c_str(), "rb");
    if (!fp) {
        onErrorData(url.c_str(), -1, nullptr);
        return;
    }

    // 获取文件大小并读取内容
    fseek(fp, 0, SEEK_END);
    size_t fileSize = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    
    std::vector<char> buffer(fileSize);
    fread(buffer.data(), 1, fileSize, fp);
    fclose(fp);

    // 直接触发加载完成回调
    onLoadedData(url.c_str(), buffer.data(), fileSize, nullptr);
}

方案2:直接传递Uint8Array到C++侧处理

如果接受直接传递文件缓冲区的方式,这种实现更简洁:

步骤1:修改Embind绑定,新增缓冲区处理重载

EMSCRIPTEN_BINDINGS(LoadTest) {
    emscripten::class_<Loader>("Loader")
        .constructor<>()
        .function("LoadAsset", &Loader::LoadAsset)
        .function("LoadAssetFromBuffer", &Loader::LoadAssetFromBuffer)
        ;
    
    emscripten::enum_<AssetType>("AssetType")
        .value("Image", AssetType::Image)
        ;
}

步骤2:添加C++侧缓冲区处理函数

#include <emscripten/val.h>

void Loader::LoadAssetFromBuffer(emscripten::val buffer, std::string objectName, AssetType type)
{
    // 将JS数组转为C++向量
    std::vector<uint8_t> data = emscripten::convertJSArrayToNumberVector<uint8_t>(buffer);
    
    // 直接处理文件内容
    onLoadedData(objectName.c_str(), data.data(), data.size(), nullptr);
}

步骤3:修改JavaScript侧调用逻辑

function onFileOpen(inputData) {
  const file = inputData.files[0];
  const reader = new FileReader();
  
  reader.onload = function(e) {
    const uint8Array = new Uint8Array(e.target.result);
    Module.GetLoaderSingleton().LoadAssetFromBuffer(uint8Array, "MyObj", Module.AssetType.Image);
  };
  
  reader.readAsArrayBuffer(file);
}
关键注意事项
  • 编译时需添加-s FORCE_FILESYSTEM=1参数,确保Emscripten虚拟文件系统API可用。
  • Blob URL属于浏览器临时资源,即使Emscripten支持,也不适合作为长期加载路径使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 20:15:38