Comunidad de diseño web y desarrollo en internet online

Abrir y guardar archivos de manera usable en AIR

Una de las grandes virtudes de Adobe AIR es ser multiplataforma. Sin embargo, esto hace que cosas como el manejo de los diálogos del sistema de archivos tengan que adaptarse a Windows, Mac y Linux, y no ofrezcan tantas funciones como sería de esperar. Por ejemplo, cada vez que abrimos un diálogo para abrir o guardar un archivo, por defecto se abre en la localización en que dejamos la ventana anterior (si le damos un objeto File nuevo, y sin ruta definida), aunque eso no siempre sea lo más conveniente.

Por ejemplo, imaginemos que estamos haciendo algún tipo de editor, que cargue archivos fuente (digamos, por ejemplo, archivos txt), y genere algún tipo de archivo de salida (como un jpg en el que insertamos el texto que hemos cargado). No nos interesa que al abrir el diálogo de guardar archivo tengamos que encontrar la carpeta donde dejamos la salida (pongamos D:/trabajos/jpgtxt/ ), y cada vez que queremos cargar tengamos que buscar el directorio de las fuentes (que puede ser C:\Documents and Settings\usuario\Mis documentos\fuentes), desde el de las salidas.

Para solucionar esto, haremos una clase que se encargue de memorizar mediante SharedObjects las rutas en las que guardamos los diferentes archivos, y de paso, un par de clases que encapsulen el proceso de cargar y guardar datos desde los diálogos del sistema, ya que realmente lo único que nos interesa es cargar un ByteArray y guardar un ByteArray, ya que al fin y al cabo, son estos objetos los que contienen la información útil de los archivos. De esta manera, podríamos recordar tanto la ruta del archivo que guardamos, como la del que cargamos, y tenerla disponible cada vez que abrimos el programa. Además nos ahorraríamos preocupaciones sobre cómo se cargan y guardan archivos, y trabajaríamos únicamente con los byteArrays que contiene los datos.

Así pues, la clase PathSaver, sería algo así:

Código :

package com.zigzah.utils
{
   import flash.events.Event;
   import flash.filesystem.File;
   import flash.net.SharedObject;
   
   /**Class to manage the file paths in a more usable way.*/
   public class PathSaver
   {
      
      private static var so:SharedObject;

      /** Returns a file from the location it was saved the last time.
       * @param id The id of the given file.
       * @param returnName if true, the function returns name of the file, else, it returns the folder.
       * @param newName if returnName is set to false, the new name of the file.
       * @return A File object, with the path it had the last time it was saved. */
            
      public static function getFile (id:String , returnName:Boolean = false , newName:String = ""):File
      {
         /*Obtenemos el SharedObject, 
         y sacamos la ruta del archivo y la del directorio.*/
         so = SharedObject.getLocal(id + "$file");
         var path: String = so.data.path;
         var parent: String = so.data.parent;
         var file:File = new File ();
         
         /*Devolvemos un nuevo archivo según si existía el so,
          y según los parámetros.*/
         if (path && parent)
         {
            if (returnName)
            {
               file = file.resolvePath(path);
            }else if (newName != "")
            {
               file = file.resolvePath(parent).resolvePath(newName);
            }else {
               file = file.resolvePath(parent)
            }
         } else
         {
            file = File.desktopDirectory.resolvePath(newName);
         }
         
         /*Indicamos que cada vez que el archivo se guarde o abra, 
         su nueva ruta quede almacenada en el so.*/
         file.addEventListener(Event.SELECT, savePath);
         
         return file;
      }
      
      
      /** This function saves the file path in a sharedObject. */
      private static function savePath (event:Event) :void
      {
         var file:File = event.target as File;
         so.data.path = file.nativePath;
         so.data.parent = file.parent.nativePath;
         so.flush();
      }

   }
}


Con esta clase, cada vez que necesitamos llamar a una archivo cuya ruta almacenó el usuario, en vez de crear el objeto File con new File (); usamos PathSaver.getFile ("IDDelArchivo");. Además, jugando con los otros parámetros, podemos conseguir distintas funcionalidades, como conseguir la ruta absoluta del archivo, o la de la carpeta que lo contiene, y obtener un nuevo nombre por defecto, que puede ser útil para guardar (como veremos en el ejemplo de abajo).

Veamos ahora una clase para cargar archivos desde el diálogo de Abrir, que hace uso de esta clase, y devuelve simplemente un ByteArray con los datos cargados:

Código :

package com.zigzah.utils
{
   import flash.events.Event;
   import flash.events.EventDispatcher;
   import flash.filesystem.File;
   import flash.filesystem.FileMode;
   import flash.filesystem.FileStream;
   import flash.net.FileFilter;
   import flash.utils.ByteArray;
   
   /**Dispatched when the user selects a file from the fileSystem, 
    * and the bytearray from the file is ready.*/
   [Event(name="bytesloaded", type="com.zigzah.utils.LoadFileEvent")]
   
   /** A utility class to get files easily from the filesystem  */
   public class FileLoader extends EventDispatcher
   {
      private var file :File
      private var filter:FileFilter
      
      /**The title of the open dialog window*/
      public var openDialog : String = "Open File";
      
      
      /**Creates a new FileLoader object, with the specified filefilter and 
       * default path, in the open dialog
       * @param filter the filefilter object that will be used to browse for files in the filesystem.
       * @param fileID the id that PathSaver will to get a file.*/
      public function FileLoader(filter : FileFilter , fileID : String = "$defaultSaveFile")
      {
         file = PathSaver.getFile(fileID);
         this.filter = filter;
         file.addEventListener(Event.SELECT, makeBytes);
      }
      
      /** Opens the browse dialog. When the user chooses a file, a 
       * LoadFileEvent will be dispatched, and it will contain the bytearray
       * retrieved by the file.
       * @see LoadFileEvent*/
      public function getBytes ():void
      {
         file.browseForOpen(openDialog ,[filter]);
      }
      
      /**Returns a ByteArray, for the LoadFileEvent.*/
      private function makeBytes (event:Event) :void
      {
         var fs:FileStream = new FileStream ();
         fs.open(event.target as File, FileMode.READ);
         var ba:ByteArray = new ByteArray ();
         fs.readBytes(ba, 0, fs.bytesAvailable);
         fs.close();
         var ev:LoadFileEvent = new LoadFileEvent();
         ev.bytes = ba;
         ev.fileName = file.name;
         this.dispatchEvent (ev);
      }

   }
}


Ésta clase también requiere LoadFileEvent, que no es más que:

Código :

package com.zigzah.utils
{
   import flash.events.Event;
   import flash.utils.ByteArray;

   public class LoadFileEvent extends Event
   {
      public var bytes : ByteArray;
      public var fileName : String;
      
      public static const BYTES_LOADED: String = "bytesloaded";
      
      
      public function LoadFileEvent(/*type:String, bubbles:Boolean=false, cancelable:Boolean=false*/)
      {
         super(LoadFileEvent.BYTES_LOADED, false, false);
      }
      
   }
}


Y ahora, otra clase para guardar ByteArrays, mediante el diálogo:

Código :

package com.zigzah.utils
{
   import flash.events.Event;
   import flash.filesystem.File;
   import flash.filesystem.FileMode;
   import flash.filesystem.FileStream;
   import flash.utils.ByteArray;
   
   /**A simple class that lets the user save a byteArray in a file, 
    * and uses PathSaver to save the file location.*/
   public class FileSaver
   {
      private var ba:ByteArray
      
      /**The title of the save dialog window.*/
      public var dialog : String = "Save File";
      
      /**Constructor.
       * @param bytes The byteArray that will be saved.*/
      public function FileSaver(bytes:ByteArray)
      {
         this.ba = bytes;
         
         
      }
      
      /**Opens a save dialog.
       * @param name the default name of the file.
       * @param fileID the id that will be used by PathSaver.*/
      public function save (name:String , fileID:String = "$defaultFile"):void
      {
         var file:File = PathSaver.getFile(fileID , false , name);
         file.addEventListener(Event.SELECT,saveBytes);
         file.browseForSave(dialog);
         
      }
      
      /**Writes the given bytes in the fileSystem.*/
      private function saveBytes (event:Event):void
      {
         var file:File = event.target as File;
         var fs:FileStream = new FileStream();
         fs.open(file , FileMode.WRITE)
         fs.writeBytes(ba);
         fs.close();
      }

   }
}


Para probarlo, podríamos hacer un simple editor de texto en Flex/AIR. Fíjense en que recuerda la ruta tanto de los archivos que cargan como de los que guardan (para eso, pruébenlo varias veces):

Código :

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
   <mx:Script>
   <![CDATA[
      import com.zigzah.utils.*;
      
      [Bindable]
      private var text:String = "";
      
      private function load (event:Event):void
      {
         var ff:FileFilter = new FileFilter ("txt" , "*.txt");
         var fldr:FileLoader = new FileLoader (ff , "loadedTXT");
         fldr.getBytes();
         fldr.addEventListener(LoadFileEvent.BYTES_LOADED,loadHandler);
      }
      
      private function loadHandler (event:LoadFileEvent):void
      {
         
         var ba:ByteArray = event.bytes;
         text = ba.readUTFBytes(ba.length);
      }
      
      private function save (event:Event):void
      {
         var ba:ByteArray = new ByteArray();
         ba.writeUTFBytes(ta.text);
         var fsaver:FileSaver = new FileSaver (ba);
         fsaver.save("newtxt.txt","savedTXT");
      }
   ]]></mx:Script>
   
      
   <mx:Button label="Load txt" click="load(event)" top="80" left="30"/>
   <mx:Button label="Save txt" click="save(event)" right="30" top="80"/>
   <mx:TextArea id="ta" text="{text}" right="30" left="30" bottom="10" top="120"/>
   
</mx:WindowedApplication>


Aquí pueden descargar los archivos del proyecto:

¿Sabes SQL? ¿No-SQL? Aprende MySQL, PostgreSQL, MongoDB, Redis y más con el Curso Profesional de Bases de Datos que empieza el martes, en vivo.

Publica tu comentario

El autor de este artículo ha cerrado los comentarios. Si tienes preguntas o comentarios, puedes hacerlos en el foro

Entra al foro y participa en la discusión

o puedes...

¿Estás registrado en Cristalab y quieres
publicar tu URL y avatar?

¿No estás registrado aún pero quieres hacerlo antes de publicar tu comentario?

Registrate