Lettura e scrittura di un ByteArray

Flash Player 9 e versioni successive, Adobe AIR 1.0 e versioni successive

La classe ByteArray fa parte del pacchetto flash.utils. Per creare un oggetto ByteArray in ActionScript 3.0, importate la classe ByteArray e chiamate il constructor, come illustrato nel seguente esempio:

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

Metodi ByteArray

Un flusso di dati significativo è organizzato in un formato che è possibile analizzare per trovare le informazioni desiderate. Un record in un file di dipendenti semplice, ad esempio, include probabilmente un numero di identificazione, un nome, un indirizzo, un numero di telefono e così via, mentre un file audio MP3 contiene un tag ID3 che identifica il titolo, l'autore, l'album, la data di pubblicazione e il genere del file che si sta scaricando. Il formato consente di conoscere l'ordine in cui prevedere i dati nel flusso di dati e di leggere tale flusso in modo intelligente.

La classe ByteArray include diversi metodi che semplificano la lettura e la scrittura in un flusso di dati. Alcuni di questi metodi includono readBytes() e writeBytes(), readInt() e writeInt(), readFloat() e writeFloat(), readObject() e writeObject(), and readUTFBytes() e writeUTFBytes(). Tali metodi consentono di leggere i dati dal flusso di dati in variabili di tipi di dati specifici, nonché di scrivere da tipi di dati specifici direttamente nel flusso di dati binario.

Ad esempio, il codice riportato di seguito legge un semplice array di stringhe e di numeri a virgola mobile e scrive ciascun elemento in un ByteArray. L'organizzazione dell'array consente al codice di chiamare i metodi ByteArray appropriati (writeUTFBytes() e writeFloat()) per la scrittura dei dati. Il modello di dati ripetitivo rende possibile la lettura ciclica dell'array.

// 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 

La proprietà position

La proprietà position consente di archiviare la posizione corrente del puntatore che indicizza il ByteArray durante lettura o scrittura. Il valore iniziale della proprietà position property è 0 (zero), come illustrato nel codice seguente:

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

Quando si legge o si scrive in un ByteArray, il metodo utilizzato aggiorna la proprietà position in modo che punti al percorso immediatamente successivo all'ultimo byte letto o scritto. Ad esempio, il codice seguente scrive una stringa in un ByteArray e successivamente la proprietà position punta al byte che segue immediatamente la stringa in 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 

Analogamente, un'operazione di lettura incrementa la proprietà position in base al numero di byte letti.

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! 

Si noti che è possibile impostare la proprietà position su una posizione specifica di ByteArray in modo che la lettura o la scrittura venga effettuata a quell'offset.

Le proprietà bytesAvailable e length

Le proprietà length e bytesAvailable indicano quanto è lungo un ByteArray e quanti byte restano dalla posizione corrente fino alla fine. Nell'esempio seguente viene illustrato come è possibile utilizzare queste proprietà. Nell'esempio viene scritta una Stringa di testo nel ByteArray e quindi il ByteArray viene letto un byte alla volta fino a che incontra il carattere “a” oppure la fine (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 
} 

La proprietà endian

I computer possono presentare differenze nel modo in cui memorizzano numeri multibyte, vale a dire numeri che richiedono più di 1 byte di memoria per la memorizzazione. Un numero intero, ad esempio, può richiedere 4 byte o 32 bit di memoria. Alcuni computer memorizzano innanzitutto il byte più significativo del numero nell'indirizzo di memoria più basso, mentre altri memorizzano prima quello meno significativo. Questo attributo di un computer, o dell'ordinamento di byte, viene definito come big endian (prima il byte più significativo) o little endian (prima il byte meno significativo). Ad esempio, il numero 0x31323334 verrebbe archiviato come segue per l'ordinamento di byte big endian e little endian, laddove a0 indica l'indirizzo di memoria più basso dei 4 byte e a3 rappresenta quello più alto:

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 proprietà endian della classe ByteArray consente di denotare l'ordine dei byte per i numeri multibyte che si sta elaborando. I valori accettabili per questa proprietà sono "bigEndian" o "littleEndian", e la classe Endian definisce le costanti BIG_ENDIAN e LITTLE_ENDIAN per impostare la proprietà endian con tali stringhe.

I metodi compress() e uncompress()

Il metodo compress() consente di comprimere un ByteArray in conformità a un algoritmo di compressione specificato come parametro dall'utente. Il metodo uncompress() consente di decomprimere un ByteArray compresso in conformità a un algoritmo di compressione. Dopo aver chiamato i metodi compress() e uncompress(), la lunghezza dell'array di byte è impostata sulla nuova lunghezza, mentre la proprietà position è impostata sulla fine.

La classe CompressionAlgorithm (AIR) definisce le costanti che potete utilizzare per specificare l'algoritmo di compressione. La classe ByteArray supporta gli algoritmi deflate (solo AIR) e zlib. L'algoritmo di compressione deflate è utilizzato in diversi formati di compressione, ad esempio zlib, gzip e alcune implementazioni zip. Il formato di dati compresso zlib è descritto in http://www.ietf.org/rfc/rfc1950.txt mentre l'algoritmo di compressione deflate è descritto in http://www.ietf.org/rfc/rfc1951.txt.

Nell'esempio seguente viene compresso un ByteArray denominato bytes mediante l'algoritmo deflate:

bytes.compress(CompressionAlgorithm.DEFLATE);

Nell'esempio seguente un ByteArray compresso viene decompresso mediante l'algoritmo deflate:

bytes.uncompress(CompressionAlgorithm.DEFLATE);

Lettura e scrittura di oggetti

I metodi readObject() e writeObject() leggono un oggetto e scrivono un oggetto in un ByteArray, codificato in linguaggio AMF (Action Message Format) serializzato. AMF è un protocollo di messaggistica proprietario creato da Adobe e utilizzato da varie classi ActionScript 3.0, incluse Netstream, NetConnection, NetStream, LocalConnection e Shared Objects.

Un indicatore di tipo a un solo byte descrive il tipo di dati codificati che seguono. AMF utilizza i seguenti 13 tipi di dati:

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

I dati codificati seguono l'indicatore di tipi a meno che tale indicatore non rappresenti un singolo valore possibile, ad esempio null, true o false, nel qual caso null'altro viene codificato.

Sono presenti due versioni di AMF: AMF0 e AMF3. AMF 0 supporta l'invio di oggetti complessi in base a riferimento e consente agli endpoint di ripristinare le relazioni tra gli oggetti. AMF 3 migliora AMF 0 mediante l'invio di oggetti trait e stringhe in base a riferimento, oltre ai riferimenti agli oggetti, e mediante il supporto di nuovi tipi di dati introdotti in ActionScript 3.0. La proprietà ByteArray.objectEcoding consente di specificare la versione di AMF utilizzata per la codifica dei dati oggetto. La classe flash.net.ObjectEncoding definisce le costanti per la specifica della versione AMF: ObjectEncoding.AMF0 e ObjectEncoding.AMF3.

Nell'esempio seguente viene chiamato writeObject() per la scrittura di un oggetto XML in un ByteArray, che viene quindi compresso mediante l'algoritmo Deflate e scritto nel file order sul desktop. Nell'esempio viene utilizzata un'etichetta per visualizzare il messaggio “Wrote order file to desktop!” (File di ordinamento scritto sul desktop), al termine nella finestra AIR.

/* The following lines, minus comment characters 
, are for Flex version: 
* <?xml version="1.0" encoding="utf-8"?> 
* <mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"  
*    creationComplete="init();"> 
* <mx:Script> 
*     <![CDATA[ 
*/ 
import flash.filesystem.*; 
import flash.utils.ByteArray; 
// import mx.controls.Label for Flex 
import fl.controls.Label // for Flash; Label component must be in Library 
// for Flex: private function init():void { 
    var bytes:ByteArray = new ByteArray(); 
    var myLabel:Label = new Label(); 
    myLabel.move(150, 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 
    outFile("order", bytes); 
    myLabel.text = "Wrote order file to desktop!"; 
// for Flex: }     // end of init()function outFile(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(); 
} 
/* Add the following lines for Flex, minus comment characters: 
*     ]]> 
* </mx:Script>             
* </mx:WindowedApplication> 
*/ 

Il metodo readObject() regge un oggetto in formato AMF serializzato da un ByteArray e lo archivia in un oggetto del tipo specificato. Nell'esempio seguente il file order sul desktop viene letto in un ByteArray (inBytes) e decompresso. Viene quindi effettuata la chiamata a readObject() per archiviarlo nell'oggetto XML orderXML. Nell'esempio viene utilizzato un costrutto di ciclo for each() per aggiungere ciascun nodo a un'area di testo per la visualizzazione. Inoltre, viene visualizzato il valore della proprietà objectEncoding insieme a un'intestazione per il contenuto del file order.

/* The following lines, minus comment characters, are for Flex version: 
* <?xml version="1.0" encoding="utf-8"?> 
* <mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" 
*     creationComplete="init();"> 
* <mx:Script> 
*         <![CDATA[ 
*/ 
import flash.filesystem.*; 
import flash.utils.ByteArray; import fl.controls.TextArea; // in Flash, TextArea component must be in Library 
// for Flex version: import mx.controls; 
// for Flex version: private function init():void {    var inBytes:ByteArray = new ByteArray(); 
    // define text area for displaying XML content 
    var myTxt:TextArea = new TextArea(); 
    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"; 
    readFile("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"; 
    }  
// for Flex version: } // end of init() // read specified file into byte array 
function readFile(fileName:String, data:ByteArray) { 
    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, 0, data.length); 
    inStream.close(); 
} 
/* Add the following lines, minus comment characters, for Flex: 
*         ]]> 
*     </mx:Script> 
*      
* </mx:WindowedApplication> 
*/