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 a fin de 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 las 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 cuando se trata de 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 discreta que desbloquea casos de uso nuevos y muy solicitados para la Web, como la implementación de bases de datos de buen rendimiento y la administración correcta de archivos temporales grandes. 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 código 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 brinda primitivas genéricas, simples y de rendimiento en las que pueden compilar componentes de mayor nivel. Las aplicaciones pueden aprovechar la mejor herramienta según 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 desarrolladores, cada una de las cuales se crea teniendo en cuenta casos prácticos específicos.

  • Algunas de estas opciones claramente no se superponen con esta propuesta, ya que solo permiten que se almacenen 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 superficie de API similar, pero se usa 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 persistentes basado en IndexedDB. Sin embargo, dado que IndexedDB es básicamente un almacén de pares clave-valor, tiene importantes limitaciones de rendimiento. 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 de CacheStorage es ampliamente compatible y se ajusta para almacenar datos de gran tamaño, como los recursos de aplicaciones web, pero los valores son inmutables.

La API de Storage Foundation es un intento de cerrar todos los vacíos 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 video, audio o imágenes Estas apps pueden descargar segmentos al disco en lugar de retenerlos 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 del que puede garantizar IDBFS

¿Qué es la API de Storage Foundation?

La API tiene dos partes principales:

  • Llamadas al sistema de archivos, que proporcionan una funcionalidad básica para interactuar con archivos y rutas de acceso a archivos
  • Controladores de archivos, 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 aloja en el objeto window y que incluye varias funciones:

  • storageFoundation.open(name): Abre el archivo con el nombre dado si existe y, de lo contrario, crea un archivo nuevo. Muestra una promesa que se resuelve con el archivo abierto.
  • storageFoundation.delete(name): Quita el archivo con el nombre dado. 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 la cantidad restante de 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.

Controladores de archivos

Se trabaja con 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, vacía) 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 longitud actual, se quitan los bytes desde el final del archivo. De lo contrario, el archivo se extenderá con bytes con valor cero.
  • NativeIOFile.read(buffer, offset): Lee el contenido del archivo en el desplazamiento determinado a través de un búfer que es el resultado de transferir el búfer determinado, que luego se desconecta. Muestra un NativeIOReadResult con el búfer transferido y la cantidad de bytes que se leyeron de forma correcta.

    Un NativeIOReadResult es un objeto que consta de dos entradas:

    • buffer: Es un objeto ArrayBufferView, que es el resultado de transferir el búfer que se pasa a read(). Es del mismo tipo y longitud que el búfer de origen.
    • readBytes: La cantidad de bytes que se leyeron correctamente en buffer. Puede ser inferior al 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 supera el 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, se desconecta. Muestra un NativeIOWriteResult con el búfer transferido y la cantidad de bytes que se escribieron de forma correcta. El archivo se extenderá si el rango de escritura excede su longitud.

    Un NativeIOWriteResult es un objeto que consta de dos entradas:

    • buffer: Es un objeto ArrayBufferView que es el resultado de transferir el búfer que se pasa a write(). Es del mismo tipo y longitud que el búfer de origen.
    • writtenBytes: La cantidad de bytes que se escribieron correctamente en buffer. Puede ser menor que el tamaño del búfer si se produce un error.

Ejemplos completos

Para que los conceptos presentados anteriormente sean más claros, aquí hay dos ejemplos completos que te guiarán a través de las diferentes etapas del ciclo de vida de los archivos de Storage Foundation.

Abrir, escribir, leer, cerrar

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

Apertura, enumeración, eliminación

// 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 probar la demostración de la API de Storage Foundation en la incorporación a continuación. Crea archivos, cámbiales el nombre, escribe en ellos y lee desde ellos, y consulta la capacidad disponible para la que solicitaste la actualización a medida que realizas 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 mediante los principios fundamentales definidos en el artículo Cómo controlar el acceso a funciones potentes de la plataforma web, 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 datos autocreados. También se limita a contextos seguros.

Control de usuarios

La cuota de almacenamiento se usará para distribuir el acceso al espacio en el disco y evitar abusos. La memoria que deseas ocupar debe solicitarse primero. Al igual que otras APIs de almacenamiento, los usuarios pueden liberar 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.

Hero image de Markus Spiske en Unsplash.