Jugando con la API del sistema de archivos HTML5

HTML5 nos proporciona una gran variedad de nuevas posibilidades, como dibujar con lienzo, implementar multimedia con las API de audio y video, etc. Una de estas herramientas, que todavía es relativamente nueva, es la API del sistema de archivos. Nos da acceso a una sección de espacio aislado del sistema de archivos local del usuario, ¡lo que nos permite llenar el vacío entre las aplicaciones web y de escritorio! En el tutorial de hoy, repasaremos los conceptos básicos de esta nueva y emocionante API, explorando las tareas más comunes del sistema de archivos. Empecemos!


Introducción

Ya no necesitamos descargar e instalar un software determinado para poder usarlo. Simplemente un navegador web y una conexión a internet nos permiten utilizar cualquier aplicación web, en cualquier momento, en cualquier lugar y en cualquier plataforma..

En resumen, las aplicaciones web son geniales; pero, en comparación con las aplicaciones de escritorio, todavía tienen una debilidad importante: no tienen una forma de interactuar y organizar los datos en una jerarquía estructurada de carpetas, un sistema de archivos real. Afortunadamente, con la nueva API del sistema de archivos, esto se puede cambiar. Esta API proporciona a las aplicaciones web acceso controlado a un "sandbox" privado del sistema de archivos privado, en el que pueden escribir y leer archivos, crear y listar directorios, etc. Aunque en el momento de escribir esto, solo el navegador Chrome de Google admite la implementación "completa" de la API del sistema de archivos, todavía merece ser estudiado como una forma potente y conveniente de almacenamiento local..

La API del sistema de archivos viene en dos versiones diferentes. La API asíncrona, que es útil para las aplicaciones normales, y la API síncrona, reservada para el uso con trabajadores web. Para los fines de este tutorial, exploraremos exclusivamente la versión asíncrona de la API.


Paso 1 - Comenzando

Su primer paso es obtener acceso al sistema de archivos HTML5 solicitando un Archivo local Objeto del sistema, utilizando el window.requestFileSystem () método global:

window.requestFileSystem (tipo, tamaño, successCallback, opt_errorCallback)

No hay forma de que una aplicación web se "rompa" más allá del directorio raíz local.

Como los dos primeros parámetros, especifique la duración y el tamaño del sistema de archivos que desea. UNA PERSISTENTE El sistema de archivos es adecuado para aplicaciones web que desean almacenar datos de usuarios de forma permanente. El navegador no lo eliminará, excepto en la solicitud explícita del usuario. UNA TEMPORAL El sistema de archivos es adecuado para las aplicaciones web que desean almacenar datos en caché, pero aún pueden funcionar si el navegador web elimina el sistema de archivos. El tamaño del sistema de archivos se especifica en bytes y debe ser un límite superior razonable en la cantidad de datos que necesita almacenar.

El tercer parámetro es una función de devolución de llamada que se activa cuando el agente de usuario proporciona con éxito un sistema de archivos. Su argumento es un Sistema de archivos objeto. Y, por último, podemos agregar una función de devolución de llamada opcional, que se llama cuando se produce un error, o se deniega la solicitud de un sistema de archivos. Su argumento es un Error de archivo objeto. Aunque este parámetro es opcional, siempre es una buena idea detectar errores para los usuarios, ya que hay una serie de lugares donde las cosas pueden salir mal..

El sistema de archivos obtenido con estas funciones depende del origen del documento que lo contiene. Todos los documentos o aplicaciones web del mismo origen (host, puerto y protocolo) comparten un sistema de archivos. Dos documentos o aplicaciones de diferentes orígenes tienen sistemas de archivos completamente distintos y separados. Un sistema de archivos está restringido a una sola aplicación y no puede acceder a los datos almacenados de otra aplicación. También está aislado del resto de los archivos en el disco duro del usuario, lo cual es bueno: no hay forma de que una aplicación web "se escape" más allá del directorio raíz local o acceda a archivos arbitrarios..

Repasemos un ejemplo:

window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem; window.requestFileSystem (window.TEMPORARY, 5 * 1024 * 1024, initFS, errorHandler); function initFS (fs) alert ("¡Bienvenido a Filesystem! It's showtime :)"); // Solo para comprobar si todo está bien :) // coloque las funciones que aprenderá más abajo aquí function errorHandler () console.log ('Ocurrió un error'); 

Esto crea un sistema de archivos temporal con 5MB de almacenamiento. Luego proporciona una función de devolución de llamada exitosa, que utilizaremos para operar nuestro sistema de archivos. Y, por supuesto, también se agrega un controlador de errores, en caso de que algo salga mal. Aquí el errorHandler () La función es demasiado genérica. Entonces, si lo desea, puede crear una versión ligeramente optimizada, lo que le da al lector un mensaje de error más descriptivo:

function errorHandler (err) var msg = 'Ocurrió un error:'; switch (err.code) caso FileError.NOT_FOUND_ERR: msg + = 'Archivo o directorio no encontrado'; descanso; caso FileError.NOT_READABLE_ERR: msg + = 'Archivo o directorio no legible'; descanso; caso FileError.PATH_EXISTS_ERR: msg + = 'El archivo o directorio ya existe'; descanso; caso FileError.TYPE_MISMATCH_ERR: msg + = 'Tipo de archivo no válido'; descanso; predeterminado: msg + = 'Error desconocido'; descanso; ; console.log (msg); ;

El objeto del sistema de archivos que obtienes tiene una nombre (un nombre único para el sistema de archivos, asignado por el navegador) y raíz Propiedad que se refiere al directorio raíz del sistema de archivos. Esto es un DirectoryEntry objeto, y puede tener directorios anidados que a su vez están representados por DirectoryEntry objetos. Cada directorio en el sistema de archivos puede contener archivos, representados por FileEntry objetos. los DirectoryEntry objeto define métodos para obtener DirectoryEntry y FileEntry objetos por nombre de ruta (opcionalmente crearán nuevos directorios o archivos si especifica un nombre que no existe). DirectoryEntry también define un createReader () método de fábrica que devuelve una DirectoryReader Objeto para listar los contenidos de un directorio. los FileEntry clase define un método para obtener el Expediente Objeto (un Blob) que representa el contenido de un archivo. A continuación, puede utilizar un FileReader objeto para leer el archivo. FileEntry define otro método para devolver una FileWriter Objeto que puedes usar para escribir contenido en un archivo..

Phhew ... suena complicado? No te preocupes Todo se aclarará a medida que avanzamos en los ejemplos a continuación..


Paso 2 - Trabajar con directorios

Obviamente, lo primero que necesita crear en un sistema de archivos es algunos grupos o directorios. Aunque el directorio raíz ya existe, no desea colocar todos sus archivos allí. Los directorios son creados por el DirectoryEntry objeto. En el siguiente ejemplo, creamos un directorio, llamado Documentos, dentro del directorio raíz:

fs.root.getDirectory ('Documents', create: true, function (dirEntry) alert ('Acaba de crear el directorio' + dirEntry.name + '.');, errorHandler);

los getDirectory () El método se utiliza tanto para leer como para crear directorios. Como primer parámetro, puede pasar un nombre o una ruta como directorio para buscar o crear. Establecemos el segundo argumento para cierto, porque estamos intentando crear un directorio, no leer uno existente. Y al final, añadimos un error de devolución de llamada..

Hasta ahora tan bueno. Tenemos un directorio; ahora vamos a agregar un subdirectorio. La función es exactamente la misma con una diferencia: cambiamos el primer argumento de 'Documentos' a 'Documentos / Música'. Suficientemente fácil; pero ¿qué pasa si desea crear una subcarpeta, Cielo, con dos carpetas principales, Imágenes y Naturaleza, dentro de Documentos ¿carpeta? Si escribesDocumentos / Imágenes / Naturaleza / Cielo'para el argumento de ruta, recibirá un error, porque no puede crear un directorio cuando su principal inmediato no existe. Una solución para esto es crear cada carpeta una por una: Imágenes dentro Documentos, Naturaleza dentro Imágenes, y entonces Cielo dentro Naturaleza. Pero este es un proceso muy lento e inconveniente. Hay una solución mejor: crear una función que creará todas las carpetas necesarias automáticamente.

function createDir (rootDir, carpetas) rootDir.getDirectory (carpetas [0], create: true, function (dirEntry) if (carpetas.length) createDir (dirEntry, Folder.slice (1));, errorHandler); ; createDir (fs.root, 'Documents / Images / Nature / Sky /'. split ('/'));

Con este pequeño truco, todo lo que tenemos que hacer es proporcionar una ruta completa que represente las carpetas que queremos crear. Ahora el Cielo El directorio se crea con éxito y puede crear otros archivos o directorios dentro de él..

Ahora es el momento de comprobar lo que tenemos en nuestro sistema de archivos. Vamos a crear un DirectoryReader objeto, y utilizar el readEntries () Método para leer el contenido del directorio..

fs.root.getDirectory ('Documents', , function (dirEntry) 
var dirReader = dirEntry.createReader (); dirReader.readEntries (función (entradas)
para (var i = 0; i < entries.length; i++) var entry = entries[i]; if (entry.isDirectory) console.log('Directory: ' + entry.fullPath); else if (entry.isFile) console.log('File: ' + entry.fullPath); , errorHandler); , errorHandler);

En el código de arriba, el esdirectorio y isFile Las propiedades se utilizan para obtener una salida diferente para directorios y archivos, respectivamente. Además, utilizamos el ruta completa propiedad para obtener la ruta completa de la entrada, en lugar de su nombre solamente.

Hay dos formas de eliminar un DirectoryEntry desde el sistema de archivos: retirar() y removeRecursively (). El primero elimina un directorio dado solo si está vacío. De lo contrario, recibirás un error..

fs.root.getDirectory ('Documents / Music', , function (dirEntry) dirEntry.remove (function () console.log ('Directorio eliminado con éxito.');, errorHandler);, errorHandler);

Si el Música La carpeta tiene archivos dentro, entonces necesita usar el segundo método, que borra recursivamente el directorio y todo su contenido..

fs.root.getDirectory ('Documents / Music', , function (dirEntry) dirEntry.removeRecursively (function () console.log ('Directorio eliminado exitosamente.');, errorHandler);, errorHandler);

Paso 3 - Trabajar con archivos

Ahora que sabemos cómo crear directorios, es hora de rellenarlos con archivos!

El siguiente ejemplo crea un vacío test.txt en el directorio raíz:

fs.root.getFile ('test.txt', create: true, exclusive: true, function (fileEntry) alert ('Un archivo' + fileEntry.name + 'se creó correctamente.');, errorHandler) ;

El primer argumento para Obtener el archivo() Puede ser una ruta absoluta o relativa, pero debe ser válida. Por ejemplo, es un error intentar crear un archivo cuando su principal inmediato no existe. El segundo argumento es un objeto literal, que describe el comportamiento de la función si el archivo no existe. En este ejemplo, crear: verdadero crea el archivo si no existe y emite un error si lo hace (exclusivo: verdadero). De lo contrario, si crear: falso, el archivo es simplemente recuperado y devuelto.

Sin embargo, tener un archivo vacío no es muy útil; Así que vamos a añadir algo de contenido dentro. Podemos usar el FileWriter objetar por esto.

fs.root.getFile ('test.txt', create: false, function (fileEntry) fileEntry.createWriter (function (fileWriter) window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder; var bb = new BlobBuilder (); bb.append ('¡La API del sistema de archivos es increíble!'); fileWriter.write (bb.getBlob ('text / plain'));, errorHandler);, errorHandler);

Arriba, recuperamos el test.txt archivo, y crear un FileWriter objeto para ello. Luego le agregamos contenido creando un nuevo BlobBuilder objeto y usando el escribir() método de FileWriter.

Vocación Obtener el archivo() solo recupera un FileEntry. No devuelve el contenido del archivo. Entonces, si queremos leer el contenido del archivo, necesitamos usar el Expediente objeto y el FileReader objeto.

fs.root.getFile ('test.txt', , function (fileEntry) fileEntry.file (function (file) var reader = new FileReader (); reader.onloadend = function (e) alert (this. resultado);; reader.readAsText (file);, errorHandler);, errorHandler);

Hemos escrito algo de contenido en nuestro archivo, pero ¿qué pasa si deseo agregar más en una fecha posterior? Para adjuntar datos a un archivo existente, la FileWriter Se usa una vez más. Podemos reposicionar el escritor al final del archivo, usando el buscar() método. buscar acepta un byte offset como argumento y establece la posición del escritor del archivo en ese offset.

fs.root.getFile ('test.txt', create: false, function (fileEntry) fileEntry.createWriter (function (fileWriter) fileWriter.seek (fileWriter.length); window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder; var bb = new BlobBuilder (); bb.append ('Sí, es!'); fileWriter.write (bb.getBlob ('text / plain'));, errorHandler);, errorHandler) ;

Para eliminar un archivo del sistema de archivos, simplemente llame entry.remove (). El primer argumento de este método es una función de devolución de llamada de parámetro cero, que se llama cuando el archivo se elimina con éxito. El segundo es una devolución de llamada de error opcional si se produce algún error.

fs.root.getFile ('test.txt', create: false, function (fileEntry) fileEntry.remove (function () console.log ('El archivo se eliminó con éxito.');, errorHandler);, errorHandler);

Paso 4 - Manipulación de archivos y directorios

FileEntry y DirectoryEntry comparte los mismos métodos API para copiar, mover y renombrar entradas. Hay dos métodos que puede utilizar para estas operaciones: copiar a() y mover a(). Ambos aceptan exactamente los mismos parámetros:

copyTo (parentDirEntry, opt_newName, opt_successCallback, opt_errorCallback); moveTo (parentDirEntry, opt_newName, opt_successCallback, opt_errorCallback);

El primer parámetro es la carpeta principal para mover / copiar la entrada en. El segundo es un nuevo nombre opcional para dar la entrada movida / copiada, que en realidad se requiere cuando copia una entrada en la misma carpeta; De lo contrario obtendrá un error. Los parámetros tercero y cuarto fueron explicados previamente..

Repasemos algunos ejemplos simples. En el siguiente, copiamos el archivo. test.txt desde el raíz al Documentos directorio.

function copy (currDir, srcEntry, destDir) currDir.getFile (srcEntry, , function (fileEntry) currDir.getDirectory (destDir, , function (dirEntry) fileEntry.copyTo (dirEntry);), errorHandler); , errorHandler);  copia (fs.root, 'test.txt', 'Documentos /');

El siguiente ejemplo se mueve. test.txt a Documentos, En lugar de copiarlo:

function move (currDir, srcEntry, dirName) currDir.getFile (srcEntry, , function (fileEntry) currDir.getDirectory (dirName, , function (dirEntry) fileEntry.moveTo (dirEntry);, errorHandler); , errorHandler);  mover (fs.root, 'test.txt', 'Documentos /');

El siguiente ejemplo renombra test.txt a texto.txt:

function rename (currDir, srcEntry, newName) currDir.getFile (srcEntry, , function (fileEntry) fileEntry.moveTo (currDir, newName);, errorHandler);  renombrar (fs.root, 'test.txt', 'text.txt');

Aprende más

En este tutorial introductorio, solo hemos arañado la superficie de las diferentes interfaces del sistema de archivos. Si desea obtener más información y profundizar en la API del sistema de archivos, debe consultar las especificaciones de W3C:

  • Archivo API: Directorios y Sistema
  • Archivo API: Writer
  • API de archivos

Ahora que tiene una comprensión básica de qué es la API del sistema de archivos y cómo se puede usar, debería ser mucho más fácil entender la documentación de la API, que puede ser un poco confusa a primera vista..


Conclusión

La API del sistema de archivos es una tecnología poderosa y fácil de usar, que brinda a los desarrolladores web toda una serie de nuevas posibilidades al crear aplicaciones web. Es cierto que todavía es bastante nuevo y no está ampliamente soportado por todos los navegadores principales, pero esto ciertamente cambiará en el futuro. Usted también podría obtener una ventaja!