File System Access API en pratique : Architecture de lecture/écriture de fichiers locaux côté navigateur

技术架构

L'évolution de l'accès aux fichiers dans le navigateur

Approche Lecture/Écriture Persistance Modèle de permissions Performance
<input type="file"> Lecture seule Aucun Moyenne
File + FileReader Lecture seule Aucun Moyenne
IndexedDB Lire/écrire des octets Même origine Faible (sérialisation)
File System Access Lire/écrire Accordée par l'utilisateur Élevée
OPFS Lire/écrire Bac à sable de même origine La plus élevée

La File System Access API confère aux navigateurs de véritables capacités de lecture/écriture de fichiers au niveau des applications natives.


Sélecteurs de fichiers : showOpenFilePicker / showSaveFilePicker

Ouvrir un fichier

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();

Enregistrer un fichier

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 : Lecture/écriture et permissions

Lecture du contenu d'un fichier

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

Écriture dans un fichier

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

Requête et demande de permissions

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;
}

Modèle de permissions : Les handles obtenus via le sélecteur reçoivent automatiquement la permission de lecture. La permission d'écriture nécessite une autorisation explicite de l'utilisateur (requestPermission affiche une invite).


Accès aux répertoires et traversée

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}`);
  }
}

Recherche récursive de fichiers

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 est un système de fichiers privé et bac à sable fourni par le navigateur pour chaque origine—aucune autorisation utilisateur nécessaire :

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

Caractéristique IndexedDB OPFS
Type de stockage Données structurées Fichiers (binaire)
Mode lecture/écriture Transactions + sérialisation E/S de fichier directe
Performance avec les gros fichiers Médiocre (surcoût de sérialisation) Excellente
Accès aléatoire ✅ (Access Handle)
Capacité Limite de même origine Limite de même origine
Support des Workers

Access Handle : Lecture/écriture aléatoire haute performance

Les fichiers OPFS prennent en charge createSyncAccessHandle(), fournissant une lecture/écriture de fichiers synchrone et basée sur la position—performance proche du système de fichiers natif :

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();

Avantage clé : Access Handle est synchrone, utilisable dans les Workers sans async/await—10 à 100 fois plus rapide que WritableStream.


Pratique : Architecture de lecture/écriture de fichiers pour la fusion PDF

Fusion PDF de ToolsKu utilise la File System Access API pour un flux de travail complet « ouvrir → traiter → enregistrer » :

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();
}

Persistance des handles de fichiers

Enregistrez les handles dans IndexedDB pour restaurer l'accès lors de la prochaine visite :

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;
}

Questions fréquentes

Support des navigateurs pour la File System Access API ?

Chrome/Edge 86+ offrent un support complet. Firefox et Safari ne la prennent pas en charge. La détection de fonctionnalités et le traitement de repli sont nécessaires.

Limites de capacité de stockage OPFS ?

OPFS partage le quota de stockage de même origine avec Cache API et IndexedDB (généralement 50%+ de l'espace disque disponible). Interrogez avec navigator.storage.estimate().

Access Handle peut-il être utilisé sur le thread principal ?

Oui, mais les E/S synchrones bloquent le thread principal. Meilleure pratique : utilisez Access Handle dans un Worker pour éviter de bloquer l'interface utilisateur.

Comment gérer l'écriture de gros fichiers ?

Utilisez l'écriture fragmentée avec WritableStream pour éviter de charger tout le fichier en mémoire :

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

Résumé

La File System Access API confère aux navigateurs des capacités de lecture/écriture de fichiers au niveau des applications natives. showOpenFilePicker/showSaveFilePicker fournissent un accès aux fichiers autorisé par l'utilisateur, FileSystemFileHandle gère les permissions de lecture/écriture, OPFS offre un stockage bac à sable sans autorisation, et Access Handle permet une lecture/écriture aléatoire synchrone haute performance. Il s'agit de l'infrastructure centrale pour construire des outils de traitement de fichiers côté navigateur.

Essayez ces outils exécutés localement dans le navigateur — aucune inscription requise →

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