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 definisce le costanti che è possibile utilizzare per specificare l'algoritmo di compressione. La classe ByteArray supporta gli algoritmi deflate (solo AIR), zlib e lzma. È possibile leggere la descrizione del formato di dati compresso zlib alle pagine e http://www.ietf.org/rfc/rfc1950.txt . L'algoritmo di compressione lzma è stato aggiunto per Flash Player 11.4 e AIR 3.4 ed è descritto qui: http://www.7-zip.org/7z.html .

L'algoritmo di compressione deflate è utilizzato in diversi formati di compressione, ad esempio zlib, gzip e alcune implementazioni zip. L'algoritmo di compressione deflate è descritto all'indirizzo http://www.ietf.org/rfc/rfc1951.txt .

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

bytes.compress(CompressionAlgorithm.LZMA);

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

bytes.uncompress(CompressionAlgorithm.LZMA);

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.

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(); 
    } 
}

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 .

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(); 
    } 
}