基于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
相关产品推荐
相关产品推荐

