Hochleistungsspeicher für Ihre Anwendung: die Storage Foundation API

Thomas Steiner
Thomas Steiner

Die Webplattform bietet Entwicklern zunehmend die Tools, die sie benötigen, um optimierte, leistungsstarke Anwendungen für das Web zu erstellen. Vor allem WebAssembly (Wasm) hat den Weg für schnelle und leistungsstarke Webanwendungen geebnet. Mit Technologien wie Emscripten können Entwickler jetzt bewährten Code im Web wiederverwenden. Um dieses Potenzial voll auszuschöpfen, müssen Entwickler in puncto Speicher dieselbe Leistungsfähigkeit und Flexibilität haben.

Hier kommt die Storage Foundation API ins Spiel. Die Storage Foundation API ist eine neue, schnelle und neutrale Speicher-API, die neue und vielfach angefragte Anwendungsfälle für das Web ermöglicht, z. B. die Implementierung leistungsfähiger Datenbanken und die reibungslose Verwaltung großer temporärer Dateien. Mit dieser neuen Benutzeroberfläche können Entwickler ihren eigenen Speicherplatz in das Web einbinden und so die Funktionslücke zwischen web- und plattformspezifischem Code verringern.

Die Storage Foundation API ist so konzipiert, dass sie einem sehr einfachen Dateisystem ähnelt. Sie bietet Entwicklern Flexibilität, da sie generische, einfache und leistungsstarke Primitive bietet, auf denen sie Komponenten höherer Ebene aufbauen können. Anwendungen können das für ihre Anforderungen am besten geeignete Tool nutzen und so das richtige Gleichgewicht zwischen Nutzerfreundlichkeit, Leistung und Zuverlässigkeit finden.

Warum braucht das Web eine weitere Speicher-API?

Die Webplattform bietet Entwicklern eine Reihe von Speicheroptionen, die jeweils für bestimmte Anwendungsfälle entwickelt wurden.

  • Einige dieser Optionen überschneiden sich eindeutig nicht mit diesem Vorschlag, da nur sehr kleine Datenmengen gespeichert werden können, z. B. Cookies oder die Web Storage API, die aus den Mechanismen sessionStorage und localStorage besteht.
  • Andere Optionen wurden bereits aus verschiedenen Gründen eingestellt, z. B. die File and Directory Entries API oder WebSQL.
  • Die File System Access API hat eine ähnliche API-Oberfläche, dient aber dazu, mit dem Dateisystem des Clients zu interagieren und Zugriff auf Daten zu gewähren, die nicht zum Ursprung oder sogar zum Browser gehören. Dieser abweichende Fokus geht mit strengeren Sicherheitsaspekten und höheren Leistungskosten einher.
  • Die IndexedDB API kann als Backend für einige Anwendungsfälle der Storage Foundation API verwendet werden. Emscripten enthält beispielsweise IDBFS, ein IndexedDB-basiertes persistentes Dateisystem. Da IndexedDB jedoch im Wesentlichen ein Schlüssel/Wert-Speicher ist, bringt dies erhebliche Leistungseinschränkungen mit sich. Darüber hinaus ist der direkte Zugriff auf Unterabschnitte einer Datei unter IndexedDB noch schwieriger und langsamer.
  • Die CacheStorage-Schnittstelle wird weithin unterstützt und ist für die Speicherung großer Datenmengen wie Webanwendungsressourcen optimiert. Die Werte sind jedoch unveränderlich.

Die Storage Foundation API ist ein Versuch, alle Lücken der vorherigen Speicheroptionen zu schließen. Sie ermöglicht die leistungsfähige Speicherung änderbarer großer Dateien, die im Ursprung der Anwendung definiert sind.

Vorgeschlagene Anwendungsfälle für die Storage Foundation API

Beispiele für Websites, die diese API verwenden können:

  • Produktivitäts- oder Kreativitäts-Apps, die große Mengen an Video-, Audio- oder Bilddaten verarbeiten Solche Anwendungen können Segmente auf das Laufwerk auslagern, anstatt sie im Speicher zu speichern.
  • Apps, die auf ein nichtflüchtiges Dateisystem angewiesen sind, auf das über Wasm zugegriffen werden kann und die eine höhere Leistung erfordern, als IDBFS garantieren kann.

Was ist die Storage Foundation API?

Die API besteht aus zwei Hauptteilen:

  • Dateisystemaufrufe, die grundlegende Funktionen zur Interaktion mit Dateien und Dateipfaden bieten.
  • Datei-Handles bieten Lese- und Schreibzugriff auf eine vorhandene Datei.

Dateisystemaufrufe

Die Storage Foundation API führt ein neues Objekt ein, storageFoundation, das sich im window-Objekt befindet und eine Reihe von Funktionen umfasst:

  • storageFoundation.open(name): Öffnet die Datei mit dem angegebenen Namen, sofern vorhanden, andernfalls wird eine neue Datei erstellt. Gibt ein Promise zurück, das mit der geöffneten Datei aufgelöst wird.
  • storageFoundation.delete(name): Die Datei mit dem angegebenen Namen wird entfernt. Gibt ein Versprechen zurück, das erfüllt wird, wenn die Datei gelöscht wird.
  • storageFoundation.rename(oldName, newName): Die Datei wird atomically vom alten zum neuen Namen umbenannt. Gibt ein Promise zurück, das beim Umbenennen der Datei aufgelöst wird.
  • storageFoundation.getAll(): Gibt ein Promise zurück, das mit einem Array aller vorhandenen Dateinamen aufgelöst wird.
  • storageFoundation.requestCapacity(requestedCapacity): Hiermit wird neue Kapazität (in Byte) für die Verwendung durch den aktuellen Ausführungskontext angefordert. Gibt ein Versprechen zurück, das mit der verbleibenden verfügbaren Kapazität aufgelöst wurde.
  • storageFoundation.releaseCapacity(toBeReleasedCapacity): Gibt die angegebene Anzahl von Byte aus dem aktuellen Ausführungskontext frei und gibt ein Versprechen zurück, das mit der verbleibenden Kapazität aufgelöst wird.
  • storageFoundation.getRemainingCapacity(): Gibt ein Promise zurück, das mit der für den aktuellen Ausführungskontext verfügbaren Kapazität aufgelöst wird.

Datei-Handler

Die folgenden Funktionen werden für die Arbeit mit Dateien verwendet:

  • NativeIOFile.close(): Schließt eine Datei und gibt ein Versprechen zurück, das aufgelöst wird, wenn der Vorgang abgeschlossen ist.
  • NativeIOFile.flush(): Synchronisiert (d. h. löscht) den In-Memory-Status einer Datei mit dem Speichergerät und gibt ein Versprechen zurück, das aufgelöst wird, wenn der Vorgang abgeschlossen ist.
  • NativeIOFile.getLength(): Gibt ein Versprechen zurück, das mit der Länge der Datei in Byte aufgelöst wird.
  • NativeIOFile.setLength(length): Legt die Länge der Datei in Byte fest und gibt ein Promise zurück, das nach Abschluss des Vorgangs aufgelöst wird. Wenn die neue Länge kleiner als die aktuelle ist, werden Byte ab dem Ende der Datei entfernt. Andernfalls wird die Datei mit Bytes mit dem Wert 0 erweitert.
  • NativeIOFile.read(buffer, offset): Liest den Inhalt der Datei am angegebenen Offset über einen Puffer, der aus der Übertragung des angegebenen Puffers resultiert und dann getrennt bleibt. Gibt eine NativeIOReadResult mit dem übertragenen Puffer und der Anzahl der Bytes zurück, die erfolgreich gelesen wurden.

    Ein NativeIOReadResult ist ein Objekt, das aus zwei Einträgen besteht:

    • buffer: Ein ArrayBufferView, das das Ergebnis der Übertragung des Zwischenspeichers ist, der an read() übergeben wurde. Er hat den gleichen Typ und die gleiche Länge wie der Quellpuffer.
    • readBytes: Die Anzahl der Byte, die erfolgreich in buffer gelesen wurden. Dieser Wert kann kleiner als die Puffergröße sein, wenn ein Fehler auftritt oder der Lesebereich über das Ende der Datei hinausreicht. Er wird auf null gesetzt, wenn der Lesebereich außerhalb der Datei liegt.
  • NativeIOFile.write(buffer, offset): Schreibt den Inhalt des angegebenen Buffers an der angegebenen Offset-Position in die Datei. Der Puffer wird übertragen, bevor Daten geschrieben werden, und bleibt daher getrennt. Gibt eine NativeIOWriteResult mit dem übertragenen Puffer und der Anzahl der Bytes zurück, die erfolgreich geschrieben wurden. Die Datei wird erweitert, wenn der Schreibbereich ihre Länge überschreitet.

    Ein NativeIOWriteResult ist ein Objekt, das aus zwei Einträgen besteht:

    • buffer: Ein ArrayBufferView, das durch die Übertragung des an write() übergebenen Buffers entsteht. Er hat denselben Typ und dieselbe Länge wie der Quellbuffer.
    • writtenBytes: Die Anzahl der Byte, die erfolgreich in buffer geschrieben wurden. Dieser Wert kann kleiner als die Puffergröße sein, wenn ein Fehler auftritt.

Vollständige Beispiele

Um die oben vorgestellten Konzepte zu veranschaulichen, finden Sie hier zwei vollständige Beispiele, die die verschiedenen Phasen des Lebenszyklus von Storage Foundation-Dateien veranschaulichen.

Öffnen, Schreiben, Lesen, Abschließen

// Open a file (creating it if needed).
const file = await storageFoundation.open('test_file');
try {
  // Request 100 bytes of capacity for this context.
  await storageFoundation.requestCapacity(100);

  const writeBuffer = new Uint8Array([64, 65, 66]);
  // Write the buffer at offset 0. After this operation, `result.buffer`
  // contains the transferred buffer and `result.writtenBytes` is 3,
  // the number of bytes written. `writeBuffer` is left detached.
  let result = await file.write(writeBuffer, 0);

  const readBuffer = new Uint8Array(3);
  // Read at offset 1. `result.buffer` contains the transferred buffer,
  // `result.readBytes` is 2, the number of bytes read. `readBuffer` is left
  // detached.
  result = await file.read(readBuffer, 1);
  // `Uint8Array(3) [65, 66, 0]`
  console.log(result.buffer);
} finally {
  file.close();
}

Öffnen, Auflisten, Löschen

// Open three different files (creating them if needed).
await storageFoundation.open('sunrise');
await storageFoundation.open('noon');
await storageFoundation.open('sunset');
// List all existing files.
// `["sunset", "sunrise", "noon"]`
await storageFoundation.getAll();
// Delete one of the three files.
await storageFoundation.delete('noon');
// List all remaining existing files.
// `["sunrise", "noon"]`
await storageFoundation.getAll();

Demo

Unten können Sie sich die Demo der Storage Foundation API ansehen. Sie können Dateien erstellen, umbenennen, in sie schreiben und aus ihnen lesen. Außerdem sehen Sie die verfügbare Kapazität, die Sie für die Aktualisierung angefordert haben, während Sie Änderungen vornehmen. Den Quellcode der Demo finden Sie auf Glitch.

Sicherheit und Berechtigungen

Das Chromium-Team hat die Storage Foundation API anhand der in Controlling Access to Powerful Web Platform Features (Zugriff auf leistungsstarke Funktionen der Webplattform steuern) definierten Grundprinzipien entworfen und implementiert, einschließlich Nutzersteuerung, Transparenz und Ergonomie.

Der Zugriff auf die Storage Foundation API folgt demselben Muster wie bei anderen modernen Speicher-APIs im Web. Das bedeutet, dass ein Ursprung nur auf selbst erstellte Daten zugreifen kann. Außerdem ist sie auf sichere Kontexte beschränkt.

Nutzersteuerung

Speicherkontingente werden verwendet, um den Zugriff auf den Speicherplatz zu verteilen und Missbrauch zu verhindern. Der Arbeitsspeicher, den Sie belegen möchten, muss zuerst angefordert werden. Wie bei anderen Speicher-APIs können Nutzer den von der Storage Foundation API belegten Speicherplatz über ihren Browser freigeben.

Nützliche Links

Danksagungen

Die Storage Foundation API wurde von Emanuel Krivoy und Richard Stotz spezifiziert und implementiert. Dieser Artikel wurde von Pete LePage und Joe Medley gelesen.

Hero-Image von Markus Spiske auf Unsplash