我在尝试使用Emscriptens IndexedDB,但无法运行。无法加载文件,"无法打开文件"。用EMSCRIPTEN_FETCH_LOAD_TO_MEMORY一切正常。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <emscripten.h>
#include <emscripten/fetch.h>
using namespace std;
void downloadSucceeded(emscripten_fetch_t *fetch)
{
printf("URL %s\n", fetch->url);
printf("bytes %llu\n", fetch->numBytes);
FILE *file = fopen("/data/test.txt", "r"); // also tried it with 'test.txt', '/test.txt', 'data/test.txt'
if (!file) {
printf("cannot open file\n");
return;
}
fclose (file);
emscripten_fetch_close(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);
}
extern "C" {
void EMSCRIPTEN_KEEPALIVE download() {
cout << "download" << endl;
emscripten_fetch_attr_t attr;
emscripten_fetch_attr_init(&attr);
strcpy(attr.requestMethod, "GET");
attr.attributes = EMSCRIPTEN_FETCH_PERSIST_FILE;
attr.onsuccess = downloadSucceeded;
attr.onerror = downloadFailed;
emscripten_fetch(&attr, "http://localhost/test.txt");
}
}
int main() {
cout << "main" << endl;
EM_ASM(
FS.mkdir('/data');
FS.mount(IDBFS, {}, '/data');
FS.syncfs(true, function (err) {
console.log("syncfs");
Module._download();
assert(!err);
});
);
emscripten_exit_with_live_runtime();
}
产量
main
syncfs
download
URL http://localhost/test.txt
bytes 185
cannot open file
建造
emcc code.cpp -o index.js --shell-file shell.html -lidbfs.js -s EXPORTED_FUNCTIONS="['_main','_download']" -s FETCH=1 -O3
如果应用程序想下载一个文件进行本地访问,但并不立即需要使用该文件,例如在前面预加载数据以便以后访问时,最好完全避免使用 EMSCRIPTEN_FETCH_LOAD_TO_MEMORY 标志,而只传递 EMSCRIPTEN_FETCH_PERSIST_FILE 标志。这将导致取回的文件直接下载到 IndexedDB,从而避免了下载完成后在内存中临时填充文件。在这种情况下,onsuccess()处理程序只会报告下载文件的总大小,但不会包含文件的数据字节。
但是我仍然可以读取数据字节,为什么?
有谁能帮帮我,我到底做错了什么?
EMSCRIPTEN_FETCH_PERSIST_FILE
实际上做了两件事。
特别是,对缓存的控制非常少:你 可以使用 EM_IDB_DELETE
来删除缓存版本,但仅此而已。
所以,看起来你不应该使用IDBFS来访问获取的文件。使用Fetch库代替,它将使用缓存版本,而不需要任何网络往返。
也可以在库中添加 -s FETCH_DEBUG
到编译线。
理解前提:通读 IndexDB基本概念 来了解 "数据库 "和 "对象存储"。
研究 Emscripten的源代码 以及我的浏览器的开发者控制台中的 "存储 "选项卡。
/data-foo-bar
会 提供对一个名为 /data-foo-bar
. 具体而言,其 FILE_DATA
对象存储(见 此处). 这里的命名是任意的、硬编码的。无文件存储在 /data-foo-bar
文件夹中存储的键如 /data-foo-bar/my-file.txt
在该对象存储内。emscripten_filesystem
数据库。里面 的 FILES
对象存储。同样,名字看起来很随意,是硬编码的。所以,你不能通过IDBFS访问Fetch的缓存,只是因为它们访问的是不同数据库中的不同存储对象,有不同的命名规则。
举个例子,下面是 FS.writeFile('/data/my-file.txt', 'hello')
结果在我的Firefox中。
这里是Fetch的缓存所在。
不幸的是,我不知道为什么Fetch网站的内容 http://localhost:8000/test.txt
被显示为一个空对象。
@yeputons: 谢谢你的解释!这帮助我理解了我的观察结果......。
EM_ASM(
var l = FS.readdir("/");
for(var i in l) {
var item = l[i];
console.log(item);
}
);
所以结论是,正如你已经提到的,不要把 "Fetch Cache "和IDBFS混合使用。如果使用EMSCRIPTEN_FETCH_PERSIST_FILE,你必须两次获取文件,1.下载并将其保存在'Fetch Cache'中,2.将其读入内存并通过fetch->data访问它。