为什么无法通过emcriptens fopen()打开保存在IndexedDB中的文件?

问题描述 投票:1回答:1

我在尝试使用Emscriptens IndexedDB,但无法运行。无法加载文件,"无法打开文件"。用EMSCRIPTEN_FETCH_LOAD_TO_MEMORY一切正常。

  1. 通过emsccripten的emsccripten_fetch_t下载文件。
  2. 通过EMSCRIPTEN_FETCH_PERSIST_FILE直接在IndexedDB中保存文件。
  3. 稍后将其加载到内存中
#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()处理程序只会报告下载文件的总大小,但不会包含文件的数据字节。

但是我仍然可以读取数据字节,为什么?

有谁能帮帮我,我到底做错了什么?

javascript c++ c fetch emscripten
1个回答
0
投票

EMSCRIPTEN_FETCH_PERSIST_FILE 实际上做了两件事。

  1. 检查文件是否已经存储在 IndexDB 中。如果是,就从那里检索,完全不麻烦服务器。
  2. 如果文件不在本地IndexDB中,就从服务器上下载并缓存在那里。

特别是,对缓存的控制非常少:你 可以使用 EM_IDB_DELETE 来删除缓存版本,但仅此而已。

回答

所以,看起来你不应该使用IDBFS来访问获取的文件。使用Fetch库代替,它将使用缓存版本,而不需要任何网络往返。

也可以在库中添加 -s FETCH_DEBUG 到编译线。

但为什么没有成功呢?

理解前提:通读 IndexDB基本概念 来了解 "数据库 "和 "对象存储"。

研究 Emscripten的源代码 以及我的浏览器的开发者控制台中的 "存储 "选项卡。

  • IDBFS挂载在 /data-foo-bar 提供对一个名为 /data-foo-bar. 具体而言,其 FILE_DATA 对象存储(见 此处). 这里的命名是任意的、硬编码的。无文件存储在 /data-foo-bar 文件夹中存储的键如 /data-foo-bar/my-file.txt 在该对象存储内。
  • 特别是,你不能访问任意的IndexDB数据库或对象存储空间。
  • 另一方面,Fetch库 店铺 文件到 emscripten_filesystem 数据库。里面FILES 对象存储。同样,名字看起来很随意,是硬编码的。

所以,你不能通过IDBFS访问Fetch的缓存,只是因为它们访问的是不同数据库中的不同存储对象,有不同的命名规则。

举个例子,下面是 FS.writeFile('/data/my-file.txt', 'hello') 结果在我的Firefox中。

Firefox Developer Tools storage tab screenshot showing <code>/data</code> mount

这里是Fetch的缓存所在。

Firefox Developer Tools storage tab screenshot showing Fetch's cache

不幸的是,我不知道为什么Fetch网站的内容 http://localhost:8000/test.txt 被显示为一个空对象。


0
投票

@yeputons: 谢谢你的解释!这帮助我理解了我的观察结果......。

  1. 我读取了IDBFS的内容,有tmp、home、dev、proc和data文件夹,但没有test.txt文件。
EM_ASM(
    var l = FS.readdir("/");
    for(var i in l) { 
        var item = l[i];
        console.log(item);
    }
    );
  1. 如果我清除浏览器缓存,然后重新加载网站,fetch->numBytes=0,并且在下次重新加载时,test.txt的数据是可用的。

所以结论是,正如你已经提到的,不要把 "Fetch Cache "和IDBFS混合使用。如果使用EMSCRIPTEN_FETCH_PERSIST_FILE,你必须两次获取文件,1.下载并将其保存在'Fetch Cache'中,2.将其读入内存并通过fetch->data访问它。

© www.soinside.com 2019 - 2024. All rights reserved.