Almacenamiento de alto rendimiento para tu app: la API de Storage Foundation

La plataforma web ofrece cada vez más a los desarrolladores las herramientas que necesitan para compilar aplicaciones de alto rendimiento y optimizadas para la Web. En particular, WebAssembly (Wasm) abrió las puertas a aplicaciones web rápidas y potentes, mientras que tecnologías como Emscripten ahora permiten a los desarrolladores reutilizar código probado en la Web. Para aprovechar realmente este potencial, los desarrolladores deben tener la misma potencia y flexibilidad en cuanto al almacenamiento.

Aquí es donde entra en juego la API de Storage Foundation. La API de Storage Foundation es una nueva API de almacenamiento rápida y sin opiniones que desbloquea casos de uso nuevos y muy solicitados para la Web, como implementar bases de datos de alto rendimiento y administrar archivos temporales grandes con elegancia. Con esta nueva interfaz, los desarrolladores pueden “traer su propio almacenamiento” a la Web, lo que reduce la brecha de funciones entre el código web y el específico de la plataforma.

La API de Storage Foundation está diseñada para parecerse a un sistema de archivos muy básico, por lo que les brinda a los desarrolladores flexibilidad, ya que proporciona primitivas genéricas, simples y de alto rendimiento en las que pueden compilar componentes de nivel superior. Las aplicaciones pueden aprovechar la mejor herramienta para sus necesidades y encontrar el equilibrio adecuado entre usabilidad, rendimiento y confiabilidad.

¿Por qué la Web necesita otra API de almacenamiento?

La plataforma web ofrece varias opciones de almacenamiento para los desarrolladores, cada una de las cuales se compila teniendo en cuenta casos de uso específicos.

  • Algunas de estas opciones claramente no se superponen con esta propuesta, ya que solo permiten almacenar cantidades muy pequeñas de datos, como las cookies o la API de Web Storage, que consta de los mecanismos sessionStorage y localStorage.
  • Otras opciones ya están obsoletas por varios motivos, como la API de File and Directory Entries o WebSQL.
  • La API de File System Access tiene una plataforma de API similar, pero su uso es para interactuar con el sistema de archivos del cliente y proporcionar acceso a datos que pueden estar fuera de la propiedad del origen o incluso del navegador. Este enfoque diferente conlleva consideraciones de seguridad más estrictas y costos de rendimiento más altos.
  • La API de IndexedDB se puede usar como backend para algunos de los casos de uso de la API de Storage Foundation. Por ejemplo, Emscripten incluye IDBFS, un sistema de archivos persistente basado en IndexedDB. Sin embargo, como IndexedDB es, en esencia, un almacén de pares clave-valor, tiene limitaciones de rendimiento significativas. Además, acceder directamente a las subsecciones de un archivo es aún más difícil y lento en IndexedDB.
  • Por último, la interfaz CacheStorage es ampliamente compatible y está ajustada para almacenar datos de gran tamaño, como recursos de aplicaciones web, pero los valores son inmutables.

La API de Storage Foundation es un intento de cerrar todas las brechas de las opciones de almacenamiento anteriores, ya que permite el almacenamiento de alto rendimiento de archivos grandes mutables definidos dentro del origen de la aplicación.

Casos de uso sugeridos para la API de Storage Foundation

Estos son algunos ejemplos de sitios que pueden usar esta API:

  • Apps de productividad o creatividad que operan con grandes cantidades de datos de imágenes, audio o video Estas apps pueden descargar segmentos en el disco en lugar de mantenerlos en la memoria.
  • Apps que dependen de un sistema de archivos persistente al que se puede acceder desde Wasm y que necesitan más rendimiento que el que puede garantizar IDBFS.

¿Qué es la API de Storage Foundation?

La API tiene dos partes principales:

  • Llamadas al sistema de archivos, que proporcionan funcionalidad básica para interactuar con archivos y rutas de acceso a archivos.
  • Controles de archivo, que proporcionan acceso de lectura y escritura a un archivo existente.

Llamadas al sistema de archivos

La API de Storage Foundation presenta un objeto nuevo, storageFoundation, que se encuentra en el objeto window y que incluye varias funciones:

  • storageFoundation.open(name): Abre el archivo con el nombre determinado si existe y, de lo contrario, crea uno nuevo. Muestra una promesa que se resuelve con el archivo abierto.
  • storageFoundation.delete(name): Quita el archivo con el nombre determinado. Muestra una promesa que se resuelve cuando se borra el archivo.
  • storageFoundation.rename(oldName, newName): Cambia el nombre del archivo del nombre anterior al nuevo de forma atómica. Muestra una promesa que se resuelve cuando se cambia el nombre del archivo.
  • storageFoundation.getAll(): Muestra una promesa que se resuelve con un array de todos los nombres de archivo existentes.
  • storageFoundation.requestCapacity(requestedCapacity): Solicita una nueva capacidad (en bytes) para que la use el contexto de ejecución actual. Muestra una promesa que se resolvió con el importe restante de la capacidad disponible.
  • storageFoundation.releaseCapacity(toBeReleasedCapacity): Libera la cantidad especificada de bytes del contexto de ejecución actual y muestra una promesa que se resuelve con la capacidad restante.
  • storageFoundation.getRemainingCapacity(): Muestra una promesa que se resuelve con la capacidad disponible para el contexto de ejecución actual.

Controles de archivos

Para trabajar con archivos, usa las siguientes funciones:

  • NativeIOFile.close(): Cierra un archivo y muestra una promesa que se resuelve cuando se completa la operación.
  • NativeIOFile.flush(): Sincroniza (es decir, borra) el estado en la memoria de un archivo con el dispositivo de almacenamiento y muestra una promesa que se resuelve cuando se completa la operación.
  • NativeIOFile.getLength(): Muestra una promesa que se resuelve con la longitud del archivo en bytes.
  • NativeIOFile.setLength(length): Establece la longitud del archivo en bytes y muestra una promesa que se resuelve cuando se completa la operación. Si la longitud nueva es menor que la actual, los bytes se quitan desde el final del archivo. De lo contrario, el archivo se extiende con bytes de valor cero.
  • NativeIOFile.read(buffer, offset): Lee el contenido del archivo en la compensación determinada a través de un búfer que es el resultado de transferir el búfer determinado, que luego se deja desconectado. Muestra un NativeIOReadResult con el búfer transferido y la cantidad de bytes que se leyeron correctamente.

    Un NativeIOReadResult es un objeto que consta de dos entradas:

    • buffer: Un ArrayBufferView, que es el resultado de transferir el búfer pasado a read(). Es del mismo tipo y longitud que el búfer de origen.
    • readBytes: Es la cantidad de bytes que se leyeron correctamente en buffer. Esto puede ser menor que el tamaño del búfer si se produce un error o si el rango de lectura se extiende más allá del final del archivo. Se establece en cero si el rango de lectura está más allá del final del archivo.
  • NativeIOFile.write(buffer, offset): Escribe el contenido del búfer determinado en el archivo en el desplazamiento determinado. El búfer se transfiere antes de que se escriban los datos y, por lo tanto, queda desconectado. Devuelve un NativeIOWriteResult con el búfer transferido y la cantidad de bytes que se escribieron correctamente. El archivo se extenderá si el rango de escritura supera su longitud.

    Un NativeIOWriteResult es un objeto que consta de dos entradas:

    • buffer: Un ArrayBufferView que es el resultado de transferir el búfer pasado a write(). Tiene el mismo tipo y la misma longitud que el búfer de origen.
    • writtenBytes: Es la cantidad de bytes que se escribieron correctamente en buffer. Si se produce un error, es posible que sea inferior al tamaño del búfer.

Ejemplos completos

Para que los conceptos que se presentaron anteriormente sean más claros, aquí tienes dos ejemplos completos que te guían por las diferentes etapas del ciclo de vida de los archivos de Storage Foundation.

Apertura, escritura, lectura y cierre

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

Cómo abrir, crear una lista y borrar

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

Demostración

Puedes jugar con la demo de la API de Storage Foundation en la incorporación que aparece a continuación. Crea archivos, cámbiales el nombre, escribe en ellos y léelos, y consulta la capacidad disponible que solicitaste actualizar a medida que realices cambios. Puedes encontrar el código fuente de la demostración en Glitch.

Seguridad y permisos

El equipo de Chromium diseñó e implementó la API de Storage Foundation con los principios básicos definidos en Controlling Access to Powerful Web Platform Features, incluidos el control del usuario, la transparencia y la ergonomía.

Siguiendo el mismo patrón que otras APIs de almacenamiento modernas en la Web, el acceso a la API de Storage Foundation está vinculado al origen, lo que significa que un origen solo puede acceder a los datos que se crearon. También se limita a contextos seguros.

Control de usuarios

La cuota de almacenamiento se usará para distribuir el acceso al espacio en disco y evitar abusos. Primero, se debe solicitar la memoria que deseas ocupar. Al igual que con otras APIs de almacenamiento, los usuarios pueden borrar el espacio que ocupa la API de Storage Foundation a través de su navegador.

Vínculos útiles

Agradecimientos

Emanuel Krivoy y Richard Stotz especificaron e implementaron la API de Storage Foundation. Pete LePage y Joe Medley revisaron este artículo.

Imagen hero de Markus Spiske en Unsplash.