Lectura y escritura de un ByteArray

Flash Player 9 y posterior, Adobe AIR 1.0 y posterior

La clase ByteArray forma parte del paquete flash.utils. Para crear un objeto ByteArray en ActionScript 3.0, importe la clase ByteArray e invoque el constructor, como se muestra en el ejemplo siguiente:

import flash.utils.ByteArray; 
var stream:ByteArray = new ByteArray();

Métodos ByteArray

Todo flujo de datos significativo se organiza en un formato que se pueda analizar para localizar la información deseada. Una ficha en un archivo sencillo de empleados, por ejemplo, probablemente incluiría un número de ID, nombre, dirección, teléfono, etc. Un archivo de audio MP3 contiene una etiqueta ID3 que identifica el título, autor, álbum, fecha de edición y género del archivo que se descarga. El formato permite saber el orden en que cabe esperar los datos en el flujo de datos. Permite leer el flujo de bytes con inteligencia.

La clase ByteArray incluye varios métodos que facilitan la lectura y escritura en un flujo de datos, entre ellos: readBytes() y writeBytes() , readInt() y writeInt() , readFloat() y writeFloat() , readObject() y writeObject() , y readUTFBytes() y writeUTFBytes() . Estos métodos permiten leer datos desde el flujo de datos en variables de tipos de datos específicos y escribir desde tipos de datos específicos directamente en el flujo de datos binarios.

En el siguiente ejemplo el código lee un conjunto sencillo de cadenas y números de punto flotante y escribe cada elemento en un ByteArray. La organización del conjunto permite que el código llame a los métodos adecuados de ByteArray ( writeUTFBytes() y writeFloat() ) para escribir los datos. El patrón de datos reiterado hace posible leer el conjunto con un bucle.

// The following example reads a simple Array (groceries), made up of strings 
// and floating-point numbers, and writes it to a ByteArray. 
 
import flash.utils.ByteArray; 
 
// define the grocery list Array 
var groceries:Array = ["milk", 4.50, "soup", 1.79, "eggs", 3.19, "bread" , 2.35] 
// define the ByteArray 
var bytes:ByteArray = new ByteArray(); 
// for each item in the array 
for (var i:int = 0; i < groceries.length; i++) { 
        bytes.writeUTFBytes(groceries[i++]); //write the string and position to the next item 
        bytes.writeFloat(groceries[i]);    // write the float 
        trace("bytes.position is: " + bytes.position);    //display the position in ByteArray 
} 
trace("bytes length is: " +  bytes.length);    // display the length 

Propiedad position

La propiedad "position" guarda la posición actual del puntero que indexa el ByteArray durante la lectura o escritura. El valor inicial de la propiedad "position" es 0 (cero) como se muestra en el código siguiente:

var bytes:ByteArray = new ByteArray(); 
trace("bytes.position is initially: " + bytes.position);     // 0 

Cuando se lee o escribe en un ByteArray, el método empleado actualiza la propiedad de posición para que apunte al lugar inmediatamente después del último byte leído o escrito. En el siguiente ejemplo, el código escribe una cadena en un ByteArray y después la propiedad de posición apunta al byte que sigue a dicha cadena en el ByteArray:

var bytes:ByteArray = new ByteArray(); 
trace("bytes.position is initially: " + bytes.position);     // 0 
bytes.writeUTFBytes("Hello World!"); 
trace("bytes.position is now: " + bytes.position);    // 12 

Asimismo, una operación de lectura incrementa la propiedad de posición con el número de bytes leídos.

var bytes:ByteArray = new ByteArray(); 
 
trace("bytes.position is initially: " + bytes.position);     // 0 
bytes.writeUTFBytes("Hello World!"); 
trace("bytes.position is now: " + bytes.position);    // 12 
bytes.position = 0; 
trace("The first 6 bytes are: " + (bytes.readUTFBytes(6)));    //Hello  
trace("And the next 6 bytes are: " + (bytes.readUTFBytes(6)));    // World! 

Obsérvese que se puede definir la propiedad de posición en un lugar concreto del ByteArray para leer o escribir en esa posición desplazada.

Propiedades bytesAvailable y length

Las propiedades length y bytesAvailable indican la longitud de un ByteArray y cuántos bytes quedan desde la posición actual hasta el final. El ejemplo siguiente muestra cómo utilizar estas propiedades. En el ejemplo se escribe una cadena de texto en el ByteArray y después se lee el ByteArray byte por byte hasta llegar al carácter “a” o al final ( bytesAvailable <= 0 ).

var bytes:ByteArray = new ByteArray(); 
var text:String = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Vivamus etc."; 
 
bytes.writeUTFBytes(text); // write the text to the ByteArray 
trace("The length of the ByteArray is: " + bytes.length);    // 70 
bytes.position = 0; // reset position 
while (bytes.bytesAvailable > 0 && (bytes.readUTFBytes(1) != 'a')) { 
    //read to letter a or end of bytes 
} 
if (bytes.position < bytes.bytesAvailable) { 
    trace("Found the letter a; position is: " + bytes.position);     // 23 
    trace("and the number of bytes available is: " + bytes.bytesAvailable);    // 47 
} 

Propiedad endian

Los ordenadores pueden variar en la forma en que guardan números que requieren varios bytes de memoria. Un entero, por ejemplo, puede necesitar 4 bytes (32 bits) de memoria. En alguno ordenadores se guarda primero el byte más significativo del número, en la dirección de memoria más baja, y en otros se guarda primero el byte menos significativo. Este atributo del ordenador, o de la ordenación de bytes, se denomina big endian (primero el byte más significativo) o little endian (primero el byte menos significativo). Por ejemplo: el número 0x31323334 se guardaría de la forma siguiente con ordenación de bytes big endian y little endian, en que a0 representa la dirección de memoria más baja de los 4 bytes y a3 representa la más alta:

Big endian

Big endian

Big endian

Big endian

a0

a1

a2

a3

31

32

33

34

Little endian

Little endian

Little endian

Little endian

a0

a1

a2

a3

34

33

32

31

La propiedad endian de la clase ByteArray permite indicar este orden de bytes para los números de varios bytes que se procesen. Los valores aceptables para esta propiedad son "bigEndian" o "littleEndian" y la clase Endian define las constantes BIG_ENDIAN y LITTLE_ENDIAN para definir la propiedad endian con estas cadenas.

Métodos compress() y uncompress()

El método compress() permite comprimir un ByteArray de acuerdo con un algoritmo de compresión especificado como parámetro. El método uncompress() permite descomprimir un ByteArray comprimido de acuerdo con un algoritmo de compresión. Tras llamar a compress() y uncompress() se define la nueva longitud del conjunto de bytes y la propiedad de posición se define en el final.

La clase CompressionAlgorithm define las constantes que se pueden utilizar para especificar el algoritmo de compresión. La clase ByteArray admite los algoritmos deflate (solo AIR), zlib e lzma. El formato de datos comprimidos zlib se describe en http://www.ietf.org/rfc/rfc1950.txt . El algoritmo lzma se añadió en Flash Player 11.4 y AIR 3.4. Encontrará una descripción en http://www.7-zip.org/7z.html .

El algoritmo de compresión deflate se utiliza en varios formatos de compresión, como zlib, gzip y algunas implementaciones de zip. El algoritmo de compresión deflate se describe en http://www.ietf.org/rfc/rfc1951.txt .

En el siguiente ejemplo se comprime un ByteArray denominado bytes con el algoritmo Izma:

bytes.compress(CompressionAlgorithm.LZMA);

En el siguiente ejemplo se descomprime un ByteArray comprimido con el algoritmo deflate:

bytes.uncompress(CompressionAlgorithm.LZMA);

Lectura y escritura de objetos

Los métodos readObject() y writeObject() leen y escriben un objeto en un ByteArray, codificado en formato de mensaje de acción (AMF) serializado. AMF es un protocolo de mensajes propio de Adobe que se utiliza en diversas clases de ActionScript 3.0, entre ellas Netstream, NetConnection, NetStream, LocalConnection y Shared Objects.

Se utiliza un marcador de un byte para describir el tipo de datos codificados que siguen. AMF utiliza los siguientes 13 tipos de datos:

value-type = undefined-marker | null-marker | false-marker | true-marker | integer-type |  
    double-type | string-type | xml-doc-type | date-type | array-type | object-type | 
    xml-type | byte-array-type

Los datos codificados siguen al marcador de tipo, a menos que el marcador represente un solo valor posible (como "null", "true" o "false"), en cuyo caso no se codifica nada más.

Existen dos versiones de AMF: AMF0 y AMF3. AMF 0 es compatible con la transmisión de objetos complejos por referencia y admite puntos finales para restaurar relaciones de objetos. AMF 3 representa una mejora de AMF 0 al enviar cadenas y características de objetos mediante referencia, además de referencias de objetos, y al admitir nuevos tipos de datos que se introdujeron en ActionScript 3.0. La propiedad ByteArray.objectEcoding especifica la versión de AMF que se utiliza para codificar los datos de objeto. La clase flash.net.ObjectEncoding define las constantes para especificar la versión de AMF: ObjectEncoding.AMF0 y ObjectEncoding.AMF3 .

En el siguiente ejemplo se llama a writeObject() para escribir un objeto XML en un ByteArray, que a continuación se comprime con el algoritmo deflate y se escribe en el archivo order en el escritorio. En el ejemplo se utiliza una etiqueta para presentar el mensaje “Wrote order file to desktop!” en la ventana de AIR una vez terminada la operación.

import flash.filesystem.*; 
import flash.display.Sprite; 
import flash.display.TextField; 
import flash.utils.ByteArray; 

public class WriteObjectExample extends Sprite 
{ 
    public function WriteObjectExample() 
    { 
        var bytes:ByteArray = new ByteArray(); 
        var myLabel:TextField = new TextField(); 
        myLabel.x = 150; 
        myLabel.y = 150; 
        myLabel.width = 200; 
        addChild(myLabel); 
          
        var myXML:XML =  
            <order> 
                <item id='1'> 
                    <menuName>burger</menuName> 
                    <price>3.95</price> 
                </item> 
                <item id='2'> 
                    <menuName>fries</menuName> 
                    <price>1.45</price> 
                </item> 
            </order>; 
          
        // Write XML object to ByteArray 
        bytes.writeObject(myXML); 
        bytes.position = 0;        //reset position to beginning 
        bytes.compress(CompressionAlgorithm.DEFLATE);    // compress ByteArray 
        writeBytesToFile("order.xml", bytes); 
        myLabel.text = "Wrote order file to desktop!"; 
    } 
     
    private function writeBytesToFile(fileName:String, data:ByteArray):void 
    { 
        var outFile:File = File.desktopDirectory; // dest folder is desktop 
        outFile = outFile.resolvePath(fileName);  // name of file to write 
        var outStream:FileStream = new FileStream(); 
        // open output file stream in WRITE mode 
        outStream.open(outFile, FileMode.WRITE); 
        // write out the file 
        outStream.writeBytes(data, 0, data.length); 
        // close it 
        outStream.close(); 
    } 
}

El método readObject() lee un objeto de un ByteArray en AMF serializado y lo guarda en un objeto del tipo especificado. En el siguiente ejemplo se lee el archivo order del escritorio para ponerlo en un ByteArray ( inBytes ), se descomprime y se llama a readObject() para guardarlo en el objeto XML orderXML . En el ejemplo se utiliza una construcción de bucle for each() para añadir cada nodo a un área de texto para su visualización. En el ejemplo se muestra también el valor de la propiedad objectEncoding junto con una cabecera para el contenido del archivo order .

import flash.filesystem.*; 
import flash.display.Sprite; 
import flash.display.TextField; 
import flash.utils.ByteArray; 
 
public class ReadObjectExample extends Sprite 
{ 
    public function ReadObjectExample() 
    { 
        var inBytes:ByteArray = new ByteArray(); 
        // define text area for displaying XML content 
        var myTxt:TextField = new TextField(); 
        myTxt.width = 550; 
        myTxt.height = 400; 
        addChild(myTxt); 
        //display objectEncoding and file heading 
        myTxt.text = "Object encoding is: " + inBytes.objectEncoding + "\n\n" + "order file: \n\n"; 
        readFileIntoByteArray("order", inBytes); 
         
        inBytes.position = 0; // reset position to beginning 
        inBytes.uncompress(CompressionAlgorithm.DEFLATE); 
        inBytes.position = 0;    //reset position to beginning 
        // read XML Object 
        var orderXML:XML = inBytes.readObject(); 
         
        // for each node in orderXML 
        for each (var child:XML in orderXML) 
        { 
            // append child node to text area 
            myTxt.text += child + "\n"; 
        }  
    } 
     
    // read specified file into byte array 
    private function readFileIntoByteArray(fileName:String, data:ByteArray):void 
    { 
        var inFile:File = File.desktopDirectory; // source folder is desktop 
        inFile = inFile.resolvePath(fileName);  // name of file to read 
        var inStream:FileStream = new FileStream(); 
        inStream.open(inFile, FileMode.READ); 
        inStream.readBytes(data); 
        inStream.close(); 
    } 
}