Dieses Thema zeigt, wie die Dateien gepackt werden, die zum Füllen von Emscriptens virtuellem Dateisystem beim Laden der Seite verwendet werden.
Es gibt zwei Alternativen, wie Dateien gepackt werden: Vorladen (preloading) und Einbetten (embedding). Beim Einbetten werden die angegebenen Dateien in der wasm-Datei gespeichert, während beim Vorladen diese in einem separaten Bundle verpackt werden. Das Einbetten von Dateien ist effizienter als das Vorladen, da keine separate Datei heruntergeladen und kopiert werden muss, aber das Vorladen ermöglicht die Option, die Daten separat zu hosten.
Emcc verwendet den Dateipackager, um die Dateien zu packen und die Dateisystem-API-Aufrufe zu generieren, die das Dateisystem zur Laufzeit erstellen und laden. Obwohl Emcc das empfohlene Tool zum Packen ist, gibt es Fälle, in denen es sinnvoll sein kann, den Dateipackager manuell auszuführen.
Mit --use-preload-plugins können Dateien automatisch basierend auf ihrer Erweiterung dekodiert werden. Weitere Informationen finden Sie unter Dateien vorladen.
Der einfachste Weg, Dateien zu packen, ist die Verwendung von emcc zur Kompilierzeit. Die Befehle preload und embed wählen ihre jeweiligen Verpackungsmethoden.
Der folgende Befehl zeigt, wie Dateien zum Vorladen gepackt werden
emcc file.cpp -o file.html --preload-file asset_dir
Der Befehl generiert file.html, file.js und file.data. Die .data-Datei enthält alle Dateien in asset_dir/ und wird von file.js geladen.
Hinweis
Das Tutorial demonstriert das Vorladen mithilfe des Testcodes hello_world_file.cpp.
Der Befehl zum Einbetten ist unten gezeigt. In diesem Fall generiert emcc file.html und file.js – der Inhalt von asset_dir/ wird direkt in die file.js eingebettet
emcc file.cpp -o file.html --embed-file asset_dir
Standardmäßig sollten die zu packenden Dateien im oder unterhalb des Verzeichnisses der Eingabeaufforderung zur Kompilierzeit verschachtelt sein. Zur Laufzeit wird dieselbe verschachtelte Dateistruktur auf das virtuelle Dateisystem abgebildet, wobei der Stamm dem Verzeichnis der Eingabeaufforderung entspricht.
Betrachten Sie beispielsweise eine Dateistruktur dir1/dir2/dir3/asset_dir/, wobei das Projekt aus dir2 kompiliert wird. Wenn wir asset_dir packen, geben wir dessen relative Position dir3/asset_dir/ an
emcc file.cpp -o file.html --preload-file dir3/asset_dir
Der Ordner ist zur Laufzeit an derselben Position dir3/asset_dir im virtuellen Dateisystem verfügbar. Wenn wir eine Datei in dir2 packen würden, wäre sie zur Laufzeit im Stammverzeichnis des virtuellen Dateisystems verfügbar.
Das Symbol @ kann verwendet werden, um gepackte Dateien von einem beliebigen Ort im lokalen Dateisystem an einen beliebigen Ort im virtuellen Dateisystem zuzuordnen. Dies wird unten in Dateispeicherorte im virtuellen Dateisystem ändern besprochen.
Sie können den Dateipackager auch manuell ausführen, indem Sie die Anweisungen oben auf der Seite file_packager befolgen.
Der Dateipackager generiert eine .data-Datei und eine .js-Datei. Die .js-Datei enthält den Code zur Verwendung der Datendatei und muss vor dem Laden Ihres Hauptkompilierten Codes geladen werden. (Fügen Sie zum Beispiel <script>-Tags am Ende Ihrer --shell-file direkt vor {{{ SCRIPT }}}` hinzu.)
Hinweis
Die Verwendung des Dateipackagers ermöglicht es Ihnen, das Packen von Dateien getrennt vom Kompilieren des Codes auszuführen.
Sie können mehrere Datendateien laden, indem Sie den Dateipackager für jede ausführen und die .js-Ausgaben laden. Ein Beispiel für dynamisches Laden finden Sie unter BananaBread (cube2/js/game-setup.js).
Standardmäßig wird die .data-Datei, die alle vorgeladenen Dateien enthält, von derselben URL wie Ihre .js-Datei geladen. In einigen Fällen kann es nützlich sein, die Datendatei an einem anderen Ort als die anderen Dateien zu haben – zum Beispiel, wenn sich Ihre .html und .js häufig ändern, möchten Sie die Datendatei möglicherweise auf einem schnellen CDN an einem anderen Ort speichern.
Dieses Modell wird unterstützt, indem die Funktion Module.locateFile angegeben wird, um die URL zurückzugeben, unter der die Datendatei gespeichert ist. Die Funktion muss in einem <script>-Element vor dem Element angegeben werden, das die Datendatei lädt.
Der Standardansatz zum Packen besteht darin, die verschachtelte Dateistruktur zur Kompilierzeit – relativ zum Verzeichnis der Eingabeaufforderung zur Kompilierzeit – direkt dem Stamm des virtuellen Dateisystems zuzuordnen. Das Symbol @ kann in einem Pfad zur Build-Zeit verwendet werden, um explizit anzugeben, wo die Ressource zur Laufzeit im virtuellen Dateisystem abgelegt wird.
Hinweis
Das @-Symbol ist erforderlich, da es manchmal nützlich ist, Dateien zu packen, die nicht unterhalb des Kompilierzeitverzeichnisses verschachtelt sind und für die es daher keine Standardzuordnung zu einem Speicherort im virtuellen Dateisystem gibt.
Wir können zum Beispiel den vorgeladenen Ordner ../../asset_dir dem Stamm des virtuellen Dateisystems (/) zuordnen, indem wir
emcc file.cpp -o file.html --preload-file ../../asset_dir@/
Wir können auch einen neuen Pfad und Dateinamen zuordnen. Um zum Beispiel die eingebettete Datei ../res/gen123.png als /main.png verfügbar zu machen, könnten wir folgendes tun
emcc file.cpp -o file.html --embed-file ../res/gen123.png@main.png
Die folgenden Zeichen dürfen in Dateinamen verwendet werden: A-Z, a-z, 0-9, das Leerzeichen und eines der Zeichen !#$%&'()+,-.;=@[]^_`{}~. Zusätzlich dürfen die folgenden Zeichen verwendet werden, wenn Ihr Host-Dateisystem sie unterstützt: "*<>?| (Windows erlaubt deren Verwendung in Dateinamen nicht). Bei der Angabe des Zeichens @ in der Befehlszeile muss es in der Form @@ maskiert werden, um die src@dst-Mapping-Notation nicht auszulösen (siehe oben). Die Zeichen /, \ und : können nicht verwendet werden.
Wichtig
Packen Sie nur die Dateien, die Ihre App tatsächlich benötigt, um die Downloadgröße zu reduzieren und die Startgeschwindigkeit zu verbessern.
Es gibt eine Option, die tatsächlich zur Laufzeit verwendeten Dateien zu protokollieren. Um diese zu verwenden, definieren Sie das Objekt Module.logReadFiles. Jede gelesene Datei wird nach stderr protokolliert.
Ein alternativer Ansatz besteht darin, FS.readFiles() in Ihrem kompilierten JavaScript zu betrachten. Dies ist ein Objekt mit Schlüsseln für alle gelesenen Dateien. Sie könnten es einfacher finden als das Protokollieren, da es Dateien anstelle von potenziell mehreren Dateizugriffen aufzeichnet.
Hinweis
Sie können das Objekt FS.readFiles() auch ändern oder vollständig entfernen. Dies kann nützlich sein, um beispielsweise zu sehen, welche Dateien zwischen zwei Zeitpunkten in Ihrer App gelesen werden.
Mit --use-preload-plugins können Dateien automatisch basierend auf ihrer Erweiterung dekodiert werden. Dies kann auch manuell durch Aufruf von emscripten_run_preload_plugins() für jede Datei erfolgen. Die Dateien bleiben in ihrer ursprünglichen Form im Dateisystem gespeichert, aber ihre dekodierte Form kann direkt verwendet werden.
Die folgenden Formate werden unterstützt
Bilder (.jpg, .jpeg, .png, .bmp): Die Dateien werden mit dem Bilddecoder des Browsers dekodiert und können dann von IMG_Load (SDL1 und SDL2 Port, die auf emscripten_get_preloaded_image_data() basieren) verwendet werden. (Setzen Sie Module.noImageDecoding auf true, um dies zu deaktivieren).
Audio (.ogg, .wav, .mp3): Die Dateien werden mit dem Audiodecoder des Browsers dekodiert und können dann mit Mix_LoadWAV (nur SDL1) verwendet werden. (Setzen Sie Module.noAudioDecoding auf true, um dies zu deaktivieren).
Dynamische Bibliotheken (.so): Die Dateien werden vorkompiliert und mit WebAssembly.instantiate instanziiert. Dies ist nützlich für Browser wie Chrome, die das asynchrone Kompilieren großer WebAssembly-Module erfordern, wenn Sie das Modul später synchron mit dlopen laden möchten. (Setzen Sie Module.noWasmDecoding auf true, um dies zu deaktivieren).
Die Testsuite enthält viele Beispiele für das Packen von Dateien und ist ein guter Ort, um nach funktionierendem Code zu suchen.