Saltar al contenido principal




Los navegadores han podido tratar con archivos y directorios por mucho tiempo. los API de archivos
proporciona funciones para representar objetos de archivo en aplicaciones web, así como para seleccionarlos a través de programación y ingresar a sus datos. A pesar de todo, en el momento en que miras más de cerca, todo lo que brilla no es oro.

La forma tradicional de tratar los archivos

Abrir archivos

Como desarrollador, puede abrir y leer archivos a través del
<input type="file">

elemento. En su forma más simple, abrir un archivo puede parecerse al ejemplo de código siguiente. los input el objeto te da un FileList, que en el caso siguiente consta de solo una
File. UNA File es un tipo específico de Blob, y se puede utilizar en cualquier contexto que un Blob pueda.

const openFile = async () => {
return new Promise((resolve) => {
const input = document.createElement('input');
input.type = 'file';
input.addEventListener('change', () => {
resolve(input.files[0]);
});
input.click();
});
};

Abrir directorios

Para abrir carpetas (o directorios), puede configurar el
<input webkitdirectory>

atributo. Aparte de eso, todo lo demás funciona igual que arriba. Pese a su nombre con prefijo de proveedor,
webkitdirectory no solo se puede usar en los navegadores Chromium y WebKit, sino además en el Edge heredado basado en EdgeHTML y en Firefox.

Guardar (más bien: descargar) archivos

Para almacenar un archivo, tradicionalmente, está limitado a descargando un archivo, que funciona gracias a la
<a download>

atributo. Dado un Blob, puede determinar el ancla href atribuir a un blob: URL que puede conseguir del
URL.createObjectURL()

método.

Precaución:
Para evitar pérdidas de memoria, siempre revoque la URL luego de la descarga.

const saveFile = async (blob) => {
const a = document.createElement('a');
a.download = 'my-file.txt';
a.href = URL.createObjectURL(blob);
a.addEventListener('click', (e) => {
setTimeout(() => URL.revokeObjectURL(a.href), 30 * 1000);
});
a.click();
};

El problema

Una desventaja masiva del descargar enfoque es que no hay forma de hacer que suceda un flujo clásico de abrir → editar → guardar, dicho de otra forma, no hay forma de Sobrescribir el archivo original. En cambio, terminas con un nuevo Copiar del archivo original en la carpeta de descargas predeterminada del sistema operativo cada vez que «guarde».

La API del sistema de archivos nativo

La API del sistema de archivos nativo hace que ambas operaciones, abrir y guardar, sean mucho más sencillas. Además posibilita verdadero ahorro, dicho de otra forma, no solo puede optar dónde guardar un archivo, sino además sobrescribir un archivo existente.

Abrir archivos

Con el API del sistema de archivos nativo, abrir un archivo es cuestión de una llamada al window.showOpenFilePicker() método. Esta llamada devuelve un identificador de archivo, del cual puede conseguir el File a través de el getFile() método.

const openFile = async () => {
try {
const [handle] = await window. showOpenFilePicker();
return handle.getFile();
} catch (err) {
console.error(err.name, err.message);
}
};

Abrir directorios

Abra un directorio llamando
window.showDirectoryPicker() que hace que los directorios se puedan elegir en el cuadro de diálogo del archivo.

Guardar archivos

Guardar archivos es igualmente sencillo. Desde un identificador de archivo, crea una secuencia de escritura mediante createWritable(), después escribe los datos de Blob llamando al flujo write() método, y en conclusión cierra la secuencia llamando a su close() método.

const saveFile = async (blob) => {
try {
const handle = await window.showSaveFilePicker({
types: [{
accept: {
},
}],
});
const writable = await handle.createWritable();
await writable.write(blob);
await writable.close();
return handle;
} catch (err) {
console.error(err.name, err.message);
}
};

Presentación de browser-nativefs

Tan estupendamente bien como la API del sistema de archivos nativo, es todavía no ampliamente disponible.

caniuse-5305250

Tabla de compatibilidad del navegador para la API del sistema de archivos nativo. (Fuente)

Es es por esto que que veo la API del sistema de archivos nativo como una mejora progresiva. Como tal, quiero usarlo cuando el navegador lo admita, y utilizar el enfoque tradicional si no; todo ello sin castigar nunca al usuario con descargas innecesarias de código JavaScript no compatible. los browser-nativefs
Library es mi respuesta a este reto.

Filosofía de diseño

Ya que es probable que la API del sistema de archivos nativo cambie en el futuro, la API del browser-nativefs no se basa en ella. Dicho de otra forma, la biblioteca no es un polyfill, sino más bien un ponyfill. Puede (estática o dinámicamente) importar exclusivamente cualquier funcionalidad que necesite para mantener su aplicación lo más pequeña viable. Los métodos disponibles son los apropiadamente nombrados
fileOpen(),
directoryOpen()y
fileSave(). Internamente, la función de biblioteca detecta si la API del sistema de archivos nativo es compatible y después importa la ruta del código respectivo.

Utilizando la biblioteca browser-nativefs

Los tres métodos son intuitivos de utilizar. Puede especificar la aceptación de su aplicación mimeTypes o archivo extensionsy establezca un multiple marca para permitir o no permitir la selección de varios archivos o directorios. Para conseguir detalles completos, consulte el
documentación de la API del browser-nativefs. El siguiente ejemplo de código muestra cómo puede abrir y guardar archivos de imagen.


import {
fileOpen,
directoryOpen,
fileSave,
} from 'https://unpkg.com/browser-nativefs';

(async () => {
const blob = await fileOpen({
mimeTypes: ['image/*'],
});


const blobs = await fileOpen({
mimeTypes: ['image/*'],
multiple: true,
});


const blobsInDirectory = await directoryOpen({
recursive: true
});


await fileSave(blob, {
fileName: 'Untitled.png',
});
})();

Manifestación

Puede ver el código anterior en acción en un manifestación en Glitch. Sus código fuente además se encuentra disponible allí. Ya que, por razones de seguridad, los subtramas de origen cruzado no pueden mostrar un selector de archivos, la demostración no se puede incrustar en este post.

La biblioteca browser-nativefs en la naturaleza

En mi tiempo libre, contribuyo un poquito a una PWA instalable llamada Excalidraw, una herramienta de pizarra que le posibilita esbozar diagramas fácilmente con una sensación de dibujado a mano. Es completamente sensible y va bastante bien en una gama de dispositivos, desde pequeños teléfonos móviles hasta computadoras con pantallas grandes. Esto significa que debe manejar archivos en todas las diversas plataformas, ya sea que admitan o no la API del sistema de archivos nativo. Esto lo convierte en un gran candidato para la biblioteca browser-nativefs.

Puedo, a modo de ejemplo, iniciar un dibujo en mi iPhone, guardarlo (técnicamente: descargarlo, dado que Safari no es compatible con la API del sistema de archivos nativo) en la carpeta Descargas de mi iPhone, abrir el archivo en mi escritorio (luego de transferirlo desde mi phone), modificar el archivo y sobrescribirlo con mis cambios, o inclusive guardarlo como un archivo nuevo.

iphone-original-5647631

Iniciar un dibujo de Excalidraw en un iPhone donde la API del sistema de archivos nativo no es compatible, pero donde se puede guardar (descargar) un archivo en la carpeta Descargas).

chrome-modify-4721146

Abrir y modificar el dibujo de Excalidraw en el escritorio donde se admite la API del sistema de archivos nativo y, de este modo, se puede ingresar al archivo mediante la API.

chrome-oversave-2788546

Sobrescribiendo el archivo original con las modificaciones al archivo de dibujo original de Excalidraw. El navegador muestra un cuadro de diálogo que me pregunta si está bien.

chrome-save-as-8162641

Guardar las modificaciones en un nuevo archivo Excalidraw. El archivo original permanece intacto.

Ejemplo de código de la vida real

A continuación, puede ver un ejemplo real de browser-nativefs tal como se utiliza en Excalidraw. Este extracto está tomado de
/src/data/json.ts. De especial interés es cómo saveAsJSON() pasa un identificador de archivo o null a browser-nativefs ‘
fileSave() , que hace que se sobrescriba cuando se le asigna un identificador, o que se guarde en un nuevo archivo si no es así.

export const saveAsJSON = async (
elements: readonly ExcalidrawElement[],
appState: AppState,
fileHandle: any,

) => {
const serialized = serializeAsJSON(elements, appState);
const blob = new Blob([serialized], {
type: "application/json",
});
const name = `${appState.name}.excalidraw`;
(window as any).handle = await fileSave(
blob,
{
fileName: name,
description: "Excalidraw file",
extensions: ["excalidraw"],
},
fileHandle || null,
);
};

export const loadFromJSON = async () => {
const blob = await fileOpen({
description: "Excalidraw files",
extensions: ["json", "excalidraw"],
mimeTypes: ["application/json"],
});
return loadFromBlob(blob);
};

Consideraciones sobre la interfaz de usuario

Ya sea en Excalidraw o en su aplicación, la interfaz de usuario debe adaptarse a la situación de soporte del navegador. Si se admite la API del sistema de archivos nativo (if ('showOpenFilePicker' in window) {}) puedes mostrar un Guardar como botón al mismo tiempo de un Salvar botón. Las capturas de pantalla a continuación muestran la diferencia entre la barra de herramientas de la aplicación principal receptiva de Excalidraw en el iPhone y en el escritorio de Chrome. Tenga en cuenta cómo en iPhone el Guardar como Falta el botón.

save-1154063

Barra de herramientas de la aplicación Excalidraw en iPhone con solo un Salvar botón.

save-save-as-7805564

Barra de herramientas de la aplicación Excalidraw en Chrome con Salvar y un enfocado Guardar como botón.

Conclusiones

Trabajar con archivos nativos técnicamente funciona en todos los navegadores modernos. En los navegadores que admiten la API del sistema de archivos nativo, puede mejorar la experiencia al permitir el verdadero guardado y sobrescritura (no solo la descarga) de archivos y al permitir que sus usuarios creen nuevos archivos donde lo deseen, todo sin dejar de ser funcional en los navegadores que sí lo hacen. no es compatible con la API del sistema de archivos nativo. los browser-nativefs le facilita la vida al lidiar con las sutilezas de la mejora progresiva y hacer que su código sea lo más simple viable.

Agradecimientos

Este post fue revisado por Joe Medley y
Kayce vascos. Gracias a colaboradores de Excalidraw
por su trabajo en el proyecto y por revisar mis Pull Requests.
Imagen de héroe por
Ilya Pavlov en Unsplash.