File System Access API in der Praxis: Browserseitige Architektur für lokales Lesen/Schreiben von Dateien

技术架构

Die Evolution des Dateizugriffs im Browser

Ansatz Lesen/Schreiben Persistenz Berechtigungsmodell Leistung
<input type="file"> Nur Lesen Keines Mittel
File + FileReader Nur Lesen Keines Mittel
IndexedDB Lesen/Schreiben von Bytes Gleiche Herkunft Niedrig (Serialisierung)
File System Access Lesen/Schreiben Vom Benutzer gewährt Hoch
OPFS Lesen/Schreiben Sandbox gleicher Herkunft Höchste

Die File System Access API verleiht Browsern echte Datei-Lese-/Schreibfähigkeiten auf Anwendungsebene.


Dateiauswahl: showOpenFilePicker / showSaveFilePicker

Eine Datei öffnen

const [fileHandle] = await window.showOpenFilePicker({
  types: [{
    description: 'Image files',
    accept: { 'image/*': ['.png', '.jpg', '.webp'] },
  }],
  multiple: false,
});

const file = await fileHandle.getFile();
const arrayBuffer = await file.arrayBuffer();

Eine Datei speichern

const handle = await window.showSaveFilePicker({
  suggestedName: 'compressed.webp',
  types: [{
    description: 'WebP Image',
    accept: { 'image/webp': ['.webp'] },
  }],
});

const writable = await handle.createWritable();
await writable.write(compressedBlob);
await writable.close();

FileSystemFileHandle: Lesen/Schreiben und Berechtigungen

Dateiinhalt lesen

const fileHandle: FileSystemFileHandle = handle;
const file = await fileHandle.getFile();

// Multiple read methods
const text = await file.text();           // Text
const buffer = await file.arrayBuffer();  // Binary
const stream = file.stream();             // Streaming

In eine Datei schreiben

async function writeFile(handle: FileSystemFileHandle, content: Blob | string | BufferSource) {
  const writable = await handle.createWritable();
  await writable.write(content);
  await writable.close();
}

Berechtigungsabfrage und -anforderung

async function verifyPermission(handle: FileSystemFileHandle, readWrite = true) {
  const options: FileSystemHandlePermissionDescriptor = { mode: readWrite ? 'readwrite' : 'read' };

  if ((await handle.queryPermission(options)) === 'granted') return true;
  if ((await handle.requestPermission(options)) === 'granted') return true;

  return false;
}

Berechtigungsmodell: Handles, die über die Auswahl erhalten wurden, erhalten automatisch Leseberechtigung. Schreibberechtigung erfordert eine ausdrückliche Benutzerautorisierung (requestPermission zeigt einen Dialog an).


Verzeichniszugriff und Traversierung

const dirHandle = await window.showDirectoryPicker();

for await (const [name, handle] of dirHandle.entries()) {
  if (handle.kind === 'file') {
    const file = await handle.getFile();
    console.log(`File: ${name}, Size: ${file.size}`);
  } else if (handle.kind === 'directory') {
    console.log(`Directory: ${name}`);
  }
}

Rekursive Dateisuche

async function* findFiles(dirHandle: FileSystemDirectoryHandle, pattern: RegExp) {
  for await (const [name, handle] of dirHandle.entries()) {
    if (handle.kind === 'file' && pattern.test(name)) {
      yield { name, handle };
    } else if (handle.kind === 'directory') {
      yield* findFiles(handle, pattern);
    }
  }
}

// Find all PDF files
for await (const pdf of findFiles(dirHandle, /\.pdf$/)) {
  console.log(pdf.name);
}

Origin Private File System (OPFS)

OPFS ist ein privates, Sandbox-basiertes Dateisystem, das der Browser für jede Herkunft bereitstellt—keine Benutzerautorisierung erforderlich:

const opfsRoot = await navigator.storage.getDirectory();

// Create a file
const fileHandle = await opfsRoot.getFileHandle('work-data.bin', { create: true });

// Create a subdirectory
const subDir = await opfsRoot.getDirectoryHandle('cache', { create: true });

// Delete a file
await opfsRoot.removeEntry('work-data.bin');

OPFS vs IndexedDB

Merkmal IndexedDB OPFS
Speichertyp Strukturierte Daten Dateien (binär)
Lese-/Schreibmodus Transaktionen + Serialisierung Direkte Datei-E/A
Leistung bei großen Dateien Schlecht (Serialisierungsaufwand) Ausgezeichnet
Wahlfreier Zugriff ✅ (Access Handle)
Kapazität Grenze gleicher Herkunft Grenze gleicher Herkunft
Worker-Unterstützung

Access Handle: Hochperformanter wahlfreier Lese-/Schreibzugriff

OPFS-Dateien unterstützen createSyncAccessHandle(), der synchronen, positionsbasierten Datei-Lese-/Schreibzugriff bietet—Leistung nahe dem nativen Dateisystem:

const fileHandle = await opfsRoot.getFileHandle('large-data.bin', { create: true });
const accessHandle = await fileHandle.createSyncAccessHandle();

// Write data at a specific position
const writeBuffer = new Uint8Array([1, 2, 3, 4, 5]);
accessHandle.write(writeBuffer, { at: 0 });

// Read data at a specific position
const readBuffer = new Uint8Array(5);
accessHandle.read(readBuffer, { at: 0 });

// Get file size
const fileSize = accessHandle.getSize();

// Flush to disk
accessHandle.flush();

// Close handle
accessHandle.close();

Hauptvorteil: Access Handle ist synchron, in Workern ohne async/await verwendbar—10-100x schneller als WritableStream.


Praxis: Lese-/Schreibarchitektur für PDF-Zusammenführung

PDF-Zusammenführung von ToolsKu verwendet die File System Access API für einen vollständigen Arbeitsablauf „Öffnen → Verarbeiten → Speichern":

async function mergePdfWorkflow() {
  // 1. User selects multiple PDFs
  const handles = await window.showOpenFilePicker({
    types: [{ accept: { 'application/pdf': ['.pdf'] } }],
    multiple: true,
  });

  // 2. Read all files
  const buffers: ArrayBuffer[] = [];
  for (const handle of handles) {
    const file = await handle.getFile();
    buffers.push(await file.arrayBuffer());
  }

  // 3. Merge processing
  const mergedPdf = await mergePdfs(buffers);

  // 4. Save result
  const saveHandle = await window.showSaveFilePicker({
    suggestedName: 'merged.pdf',
    types: [{ accept: { 'application/pdf': ['.pdf'] } }],
  });
  const writable = await saveHandle.createWritable();
  await writable.write(mergedPdf);
  await writable.close();
}

Datei-Handles persistieren

Speichern Sie Handles in IndexedDB, um den Zugriff beim nächsten Besuch wiederherzustellen:

async function saveHandle(key: string, handle: FileSystemFileHandle) {
  const db = await openDB('file-handles', 1, {
    upgrade(db) { db.createObjectStore('handles'); },
  });
  await db.put('handles', handle, key);
}

async function loadHandle(key: string) {
  const db = await openDB('file-handles', 1);
  const handle: FileSystemFileHandle = await db.get('handles', key);

  if (await verifyPermission(handle, true)) {
    return handle;
  }
  return null;
}

Häufige Fragen

Browserunterstützung für die File System Access API?

Chrome/Edge 86+ bieten volle Unterstützung. Firefox und Safari unterstützen sie nicht. Feature-Erkennung und Fallback-Behandlung sind erforderlich.

OPFS-Speicherkapazitätsgrenzen?

OPFS teilt sich das Speicherkontingent gleicher Herkunft mit der Cache API und IndexedDB (typischerweise 50%+ des verfügbaren Speicherplatzes). Abfragen mit navigator.storage.estimate().

Kann Access Handle im Hauptthread verwendet werden?

Ja, aber synchrone E/A blockiert den Hauptthread. Best Practice: Verwenden Sie Access Handle in einem Worker, um die Benutzeroberfläche nicht zu blockieren.

Wie geht man mit dem Schreiben großer Dateien um?

Verwenden Sie gestückeltes Schreiben mit WritableStream, um zu vermeiden, dass die gesamte Datei in den Speicher geladen wird:

const writable = await handle.createWritable();
for await (const chunk of readableStream) {
  await writable.write(chunk);
}
await writable.close();

Zusammenfassung

Die File System Access API verleiht Browsern Datei-Lese-/Schreibfähigkeiten auf Anwendungsebene. showOpenFilePicker/showSaveFilePicker bieten benutzerautorisierten Dateizugriff, FileSystemFileHandle verwaltet Lese-/Schreibberechtigungen, OPFS bietet autorisierungsfreien Sandbox-Speicher und Access Handle ermöglicht hochperformanten synchronen wahlfreien Lese-/Schreibzugriff. Dies ist die Kerninfrastruktur für den Aufbau browserseitiger Dateiverarbeitungstools.

Probiere diese browser-lokalen Tools aus — keine Registrierung erforderlich →

#File System Access#OPFS#文件句柄#本地文件#读写权限