如何从C++编译的WebAssembly代码中触发浏览器下载?
我一直希望不通过JavaScript端实现该需求,目前还未找到满意的方案。Fetch API看起来是不错的方向,但我还没找到在浏览器中触发下载zip文件的方法。
以下是Emscripten代码片段,但它看起来仅适用于某种本地文件场景:
#include <stdio.h> #include <string.h> #include <emscripten/fetch.h> void downloadSucceeded(emscripten_fetch_t *fetch) { printf("Finished downloading %llu bytes from URL %s.\n", fetch->numBytes, fetch->url); // The data is now available at fetch->data[0] through fetch->data[fetch->numBytes-1]; emscripten_fetch_close(fetch); // Free data associated with the fetch. } void downloadFailed(emscripten_fetch_t *fetch) { printf("Downloading %s failed, HTTP failure status code: %d.\n", fetch->url, fetch->status); emscripten_fetch_close(fetch); // Also free data on failure. } int main() { emscripten_fetch_attr_t attr; emscripten_fetch_attr_init(&attr); strcpy(attr.requestMethod, "GET"); attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY; attr.onsuccess = downloadSucceeded; attr.onerror = downloadFailed; emscripten_fetch(&attr, "myfile.dat"); }
解决方案
你可以通过在Emscripten的fetch成功回调中嵌入内联JS逻辑实现浏览器触发下载,不需要额外编写独立的前端JS代码,符合你不想在JS侧单独实现需求的预期,修改后的代码如下:
#include <stdio.h> #include <string.h> #include <emscripten/fetch.h> #include <emscripten/em_asm.h> void downloadSucceeded(emscripten_fetch_t *fetch) { printf("已完成从URL %s 下载%llu字节。\n", fetch->url, fetch->numBytes); // 内联JS逻辑触发浏览器下载,无需额外维护独立JS文件 EM_ASM( { // 读取C侧传递的二进制数据、长度和文件名 const uint8_t* data = $0; size_t dataLen = $1; const char* fileName = UTF8ToString($2); // 生成可下载的Blob对象,指定zip类型 const blob = new Blob([new Uint8Array(Module.HEAPU8.buffer, data, dataLen)], { type: 'application/zip' }); const objUrl = URL.createObjectURL(blob); // 创建临时a标签触发下载 const downloadLink = document.createElement('a'); downloadLink.href = objUrl; downloadLink.download = fileName; document.body.appendChild(downloadLink); downloadLink.click(); // 异步清理临时资源 setTimeout(() => { document.body.removeChild(downloadLink); URL.revokeObjectURL(objUrl); }, 0); }, fetch->data, fetch->numBytes, "output.zip" // 可自定义下载保存的文件名 ); emscripten_fetch_close(fetch); // 释放fetch关联的内存数据 } void downloadFailed(emscripten_fetch_t *fetch) { printf("下载 %s 失败,HTTP错误状态码:%d。\n", fetch->url, fetch->status); emscripten_fetch_close(fetch); } int main() { emscripten_fetch_attr_t attr; emscripten_fetch_attr_init(&attr); strcpy(attr.requestMethod, "GET"); attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY; attr.onsuccess = downloadSucceeded; attr.onerror = downloadFailed; emscripten_fetch(&attr, "your_target_file.zip"); // 替换为实际的zip文件请求地址 }
补充说明
- 编译时直接使用常规emcc命令即可,不需要额外引入外部JS文件,所有逻辑都封装在C代码侧
- 触发下载的逻辑符合浏览器安全规范,不会被常规的弹窗拦截规则拦截
- 如果需要下载非zip格式的文件,只需要修改Blob的type参数和下载文件名后缀即可
内容的提问来源于stack exchange,提问作者Richard Lalancette
相关产品推荐
相关产品推荐

