Start · Sprachen · JavaScript · Referenz · FileSystemSyncAccessHandle

FileSystemSyncAccessHandle

Schnittstelle

Synchroner, besonders schneller Lese- und Schreibzugriff auf eine Datei im Origin Private File System (OPFS). Nur in dedizierten Web Workern verfügbar.

seit JavaScript Baseline 2023: Chrome 102+, Edge 102+, Firefox 111 Kategorie: webapi

Signatur

interface FileSystemSyncAccessHandle

Beschreibung

FileSystemSyncAccessHandle gehört zur File System API und bietet synchronen, sehr performanten Zugriff auf eine einzelne Datei im Origin Private File System (OPFS). Das OPFS ist ein privater Speicherbereich pro Origin. Der Nutzer sieht ihn nicht, und es gibt keine Berechtigungsdialoge. Einen Handle bekommst du nur über FileSystemFileHandle.createSyncAccessHandle(). Diese Methode steht ausschließlich in dedizierten Web Workern zur Verfügung, damit synchrone Ein- und Ausgaben nie den Haupt-Thread blockieren.

Die Schnittstelle ist für Anwendungen gedacht, die viele kleine Lese- und Schreibzugriffe an beliebigen Stellen einer Datei brauchen. Typische Beispiele sind Datenbanken, die nach WebAssembly übersetzt wurden (etwa SQLite-Wasm), Emulatoren, Spiele-Engines oder Editoren für große Binärdateien. Anders als FileSystemWritableFileStream schreibst du direkt in die Datei. Es gibt keine temporäre Kopie und keinen Tausch der Datei beim Schließen.

Die Methoden im Überblick:

  • read(buffer, { at }): liest Bytes in einen ArrayBuffer bzw. eine ArrayBufferView und gibt die Anzahl gelesener Bytes zurück.
  • write(buffer, { at }): schreibt Bytes aus einem Puffer in die Datei und gibt die Anzahl geschriebener Bytes zurück.
  • getSize(): gibt die aktuelle Dateigröße in Bytes zurück.
  • truncate(newSize): kürzt die Datei auf newSize Bytes oder verlängert sie auf diese Größe.
  • flush(): sorgt dafür, dass alle Änderungen auf dem Datenträger gespeichert werden.
  • close(): schließt den Handle und gibt die exklusive Sperre auf die Datei frei.

Solange ein Sync-Access-Handle geöffnet ist, hält er standardmäßig eine exklusive Sperre auf die Datei. Andere Handles (auch in anderen Tabs) können die Datei erst öffnen, wenn du close() aufgerufen hast.

Parameter

Name Typ Default Beschreibung
$buffer Pflicht ArrayBuffer | ArrayBufferView Für read() der Zielpuffer, für write() der Quellpuffer mit den Bytes.
$options.at number Aktuelle Position des Dateizeigers Byte-Offset in der Datei, ab dem read() bzw. write() arbeitet.
$newSize Pflicht number Neue Dateigröße in Bytes für truncate().

Rückgabewert

Typ
FileSystemSyncAccessHandle
Beschreibung
Eine Instanz bekommst du über await fileHandle.createSyncAccessHandle(). read(), write() und getSize() geben eine number zurück, truncate(), flush() und close() geben undefined zurück. In aktuellen Browsern arbeiten alle diese Methoden synchron.

Beispiele

Handle im Worker erzeugen, Text schreiben und wieder lesen

// worker.js – läuft in einem dedizierten Web Worker
const run = async () => {
  const root = await navigator.storage.getDirectory();
  const fileHandle = await root.getFileHandle('notizen.txt', { create: true });
  const access = await fileHandle.createSyncAccessHandle();

  try {
    const encoder = new TextEncoder();
    const decoder = new TextDecoder();

    // Datei leeren und neu beschreiben
    access.truncate(0);
    const written = access.write(encoder.encode('Hallo OPFS!'), { at: 0 });
    access.flush();

    // Gesamten Inhalt zurücklesen
    const size = access.getSize();
    const buffer = new Uint8Array(size);
    access.read(buffer, { at: 0 });

    postMessage({ written, size, text: decoder.decode(buffer) });
  } finally {
    access.close(); // Sperre freigeben
  }
};

run();
{ written: 11, size: 11, text: 'Hallo OPFS!' }

Daten an eine Protokolldatei anhängen

// worker.js
const appendLog = async (line) => {
  const root = await navigator.storage.getDirectory();
  const fileHandle = await root.getFileHandle('app.log', { create: true });
  const access = await fileHandle.createSyncAccessHandle();

  try {
    const bytes = new TextEncoder().encode(`${new Date().toISOString()} ${line}\n`);
    // Ans Dateiende schreiben
    access.write(bytes, { at: access.getSize() });
    access.flush();
    return access.getSize();
  } finally {
    access.close();
  }
};

self.onmessage = async (event) => {
  const newSize = await appendLog(event.data);
  postMessage(`Log-Größe: ${newSize} Bytes`);
};
Log-Größe: 42 Bytes

Wahlfreier Zugriff: Datensätze fester Länge lesen und ändern

// worker.js – einfache "Datenbank" mit 8-Byte-Datensätzen (Float64)
const RECORD = 8;

const main = async () => {
  const root = await navigator.storage.getDirectory();
  const fh = await root.getFileHandle('werte.bin', { create: true });
  const access = await fh.createSyncAccessHandle();

  try {
    // Platz für 100 Datensätze reservieren
    access.truncate(100 * RECORD);

    // Datensatz Nr. 42 schreiben
    const out = new Float64Array([3.14159]);
    access.write(out, { at: 42 * RECORD });

    // Datensatz Nr. 42 wieder lesen
    const inBuf = new Float64Array(1);
    access.read(inBuf, { at: 42 * RECORD });
    console.log(inBuf[0], access.getSize());
  } finally {
    access.close();
  }
};

main();
3.14159 800

// Wichtig · Fallstricke

  • Nur in dedizierten Workern: createSyncAccessHandle() gibt es weder im Haupt-Thread noch in Shared Workern oder Service Workern. Im Fenster-Kontext ist die Methode nicht definiert.
  • Nur im OPFS: Für Dateien, die der Nutzer über showOpenFilePicker() ausgewählt hat, funktioniert die Methode nicht. Sie ist nur für Handles aus navigator.storage.getDirectory() verfügbar.
  • Exklusive Sperre: Ein offener Handle sperrt die Datei. Ein zweiter Aufruf von createSyncAccessHandle() wirft dann eine NoModificationAllowedError. Rufe close() deshalb immer auf, am besten in einem finally-Block. Chrome kennt zusätzlich die Option { mode: 'readwrite-unsafe' } bzw. 'read-only', damit mehrere Handles gleichzeitig auf eine Datei zugreifen können. Andere Browser unterstützen diese Option noch nicht überall.
  • Historische Asynchronität: In frühen Chrome-Versionen (bis 107) gaben getSize(), truncate(), flush() und close() ein Promise zurück. Falls du ältere Browser unterstützen musst, schreibe await davor. Bei synchronen Rückgabewerten schadet das nicht.
  • Offset angeben: Ohne at arbeiten read() und write() ab einem internen Dateizeiger, der nach jedem Aufruf weiterwandert. Das führt leicht zu Fehlern. Gib den Offset deshalb besser immer ausdrücklich an.
  • Speicherkontingent: Geht beim Schreiben der Speicherplatz aus, wird eine QuotaExceededError geworfen. Wie viel Platz noch frei ist, erfährst du mit navigator.storage.estimate().
  • Die Daten im OPFS können gelöscht werden, wenn der Browser Speicher freigibt. Mit navigator.storage.persist() kannst du beantragen, dass sie dauerhaft erhalten bleiben.