API de Acceso al Sistema de Archivos en la Práctica: Arquitectura de Lectura/Escritura de Archivos Locales en el Navegador

技术架构

La Evolución del Acceso a Archivos en el Navegador

Enfoque Lectura/Escritura Persistencia Modelo de Permisos Rendimiento
<input type="file"> Solo lectura Ninguno Medio
File + FileReader Solo lectura Ninguno Medio
IndexedDB Leer/escribir bytes Mismo origen Bajo (serialización)
File System Access Leer/escribir Concedido por el usuario Alto
OPFS Leer/escribir Aislamiento de mismo origen El más alto

La API de Acceso al Sistema de Archivos otorga a los navegadores verdaderas capacidades de lectura/escritura de archivos a nivel de aplicación nativa.


Selectores de Archivos: showOpenFilePicker / showSaveFilePicker

Abrir un Archivo

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

Guardar un Archivo

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: Lectura/Escritura y Permisos

Leer el Contenido de un Archivo

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

Escribir en un Archivo

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

Consulta y Solicitud de Permisos

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

Modelo de permisos: Los handles obtenidos mediante el selector obtienen automáticamente permiso de lectura. El permiso de escritura requiere autorización explícita del usuario (requestPermission muestra un diálogo).


Acceso a Directorios y Recorrido

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

Búsqueda Recursiva de Archivos

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 es un sistema de archivos privado y aislado proporcionado por el navegador para cada origen—no se necesita autorización del usuario:

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

Característica IndexedDB OPFS
Tipo de almacenamiento Datos estructurados Archivos (binario)
Modo de lectura/escritura Transacciones + serialización E/S de archivo directa
Rendimiento con archivos grandes Deficiente (sobrecarga de serialización) Excelente
Acceso aleatorio ✅ (Access Handle)
Capacidad Límite de mismo origen Límite de mismo origen
Soporte en Workers

Access Handle: Lectura/Escritura Aleatoria de Alto Rendimiento

Los archivos OPFS admiten createSyncAccessHandle(), que proporciona lectura/escritura de archivos síncrona y basada en posición—rendimiento cercano al sistema de archivos nativo:

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

Ventaja clave: Access Handle es síncrono, utilizable en Workers sin async/await—10-100 veces más rápido que WritableStream.


Práctica: Arquitectura de Lectura/Escritura de Archivos para Fusión de PDF

Fusión de PDF de ToolsKu utiliza la API de Acceso al Sistema de Archivos para un flujo de trabajo completo de "abrir → procesar → guardar":

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

Persistir Handles de Archivos

Guarde los handles en IndexedDB para restaurar el acceso en la próxima visita:

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

Preguntas Frecuentes

¿Soporte del navegador para la API de Acceso al Sistema de Archivos?

Chrome/Edge 86+ tienen soporte completo. Firefox y Safari no la soportan. Se requiere detección de funcionalidades y manejo de alternativas.

¿Límites de capacidad de almacenamiento de OPFS?

OPFS comparte la cuota de almacenamiento de mismo origen con Cache API e IndexedDB (típicamente 50%+ del espacio disponible en disco). Consulte con navigator.storage.estimate().

¿Se puede usar Access Handle en el hilo principal?

Sí, pero la E/S síncrona bloquea el hilo principal. Mejor práctica: use Access Handle en un Worker para evitar bloquear la interfaz de usuario.

¿Cómo manejar la escritura de archivos grandes?

Use escritura fragmentada con WritableStream para evitar cargar todo el archivo en memoria:

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

Resumen

La API de Acceso al Sistema de Archivos otorga a los navegadores capacidades de lectura/escritura de archivos a nivel de aplicación nativa. showOpenFilePicker/showSaveFilePicker proporcionan acceso a archivos autorizado por el usuario, FileSystemFileHandle gestiona los permisos de lectura/escritura, OPFS proporciona almacenamiento aislado sin autorización, y Access Handle permite lectura/escritura aleatoria síncrona de alto rendimiento. Esta es la infraestructura central para construir herramientas de procesamiento de archivos en el navegador.

Prueba estas herramientas que se ejecutan en tu navegador — no requieren registro →

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