Odczytywanie i zapisywanie obiektu ByteArray

Flash Player 9 i nowsze wersje, Adobe AIR 1.0 i nowsze wersje

Klasa ByteArray należy do pakietu flash.utils. Aby utworzyć obiekt ByteArray w programie ActionScript 3.0, należy zaimportować klasę ByteArray i wywołać konstruktora zgodnie z następującym przykładem:

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

ByteArray, metody

Każdy poprawny strumień danych jest uporządkowany w formacie, który można analizować w celu wyszukania żądanych informacji. Rekord w prostym pliku pracownika może na przykład zawierać imię i nazwisko, numer dowodu osobistego, adres, numer telefonu, itd. Plik audio MP3 zawiera znacznik ID3 określający autora, album, datę publikacji oraz gatunek pobieranego pliku. Format pozwala na określenie kolejności danych w strumieniu danych. Umożliwia również inteligentne odczytanie strumienia bajtów.

Klasa ByteArray zawiera kilka metod ułatwiających odczytywanie i zapisywanie danych do strumienia danych. Niektóre z tych metod zawierają readBytes() i writeBytes() , readInt() i writeInt() , readFloat() i writeFloat() , readObject() i writeObject() , oraz readUTFBytes() i writeUTFBytes() . Te metody umożliwiają odczytanie danych ze strumienia danych jako zmienne określonych typów danych i zapisanie określonych typów danych bezpośrednio do binarnego strumienia danych.

Na przykład, następujący kod odczytuje prostą tablicę ciągów i liczby zmiennoprzecinkowe i zapisuje każdy element do ByteArray. Organizacja tablic umożliwia kodowi wywołanie odpowiedniej metody ByteArray ( writeUTFBytes() i writeFloat() ) w celu zapisania danych. Powtarzający się wzór danych umożliwia odczytanie tablicy w pętli.

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

Właściwość position

Właściwość position zapisuje bieżącą pozycję wskaźnika indeksującego obiekt ByteArray podczas odczytywania i zapisywania. Początkowa wartość właściwości position to 0 (zero) zgodnie z następującym kodem:

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

Podczas odczytywania i zapisywania do obiektu ByteArray użyte metody aktualizują właściwość position, tak aby wskazywała miejsce bezpośrednio po ostatnim zapisanym lub odczytanym bajcie. Na przykład, następujący kod zapisuje ciąg w obiekcie ByteArray, a następnie właściwość position wskazuje bajt bezpośrednio po łańcuchu w obiekcie 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 

Podobnie, czynność odczytywania powoduje zwiększanie wartości właściwości position o liczbę odczytanych bajtów.

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! 

Właściwość position można ustawić na określone położenie w obiekcie ByteArray w celu odczytania lub zapisania przy tym przesunięciu.

Właściwości bytesAvailable i length

Właściwości length i bytesAvailable informują o długości obiektu ByteArray oraz liczbie znajdujących się w nim bajtów od położenia początkowego od końca. W następującym przykładzie przedstawiony jest sposób użycia tych właściwości. W przykładzie w obiekcie ByteArray zapisany jest ciąg tekstu String, który jest następnie odczytany jako kolejne bajty do czasu napotkania znaku „a” lub zakończenia ( 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 
} 

Właściwość endian

Komputery mogą różnić się w sposobie zapisywania liczb wielobajtowych, tj. liczb wymagających do zapisania więcej niż jeden bajt pamięci. Liczba całkowita może na przykład zajmować w pamięci 4 bajty lub 32 bity. Niektóre komputery zapisują w pierwszej kolejności najbardziej znaczące bajty w najniższych adresach pamięci, inne natomiast najmniej znaczące bajty. Ten atrybut komputera lub kolejność bajtów określa się jako big endian (najbardziej znaczące bajty najpierw) lub little endian (najmniej znaczące bajty najpierw). Na przykład, liczba 0x31323334 będzie zapisana w następujący sposób zgodnie z kolejnością bajtów big endian i little endian, gdzie a0 przedstawia najniższy adres pamięci 4 bajtów, a a3 przedstawia najwyższy:

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

Właściwość endian klasy ByteArray umożliwia określenie kolejności bajtów dla przetwarzanych liczb wielobajtowych. Dopuszczalne wartości to "bigEndian" lub "littleEndian" - klasa Endian definiuje stałe BIG_ENDIAN i LITTLE_ENDIAN do ustawienia wartości endian za pomocą tych ciągów.

Metody compress() i uncompress()

Metoda compress() umożliwia kompresje obiektu ByteArray zgodnie z algorytmem kompresji określonego jako parametr. Metoda uncompress() umożliwia cofnięcie kompresji skompresowanego ByteArray zgodnie z algorytmem kompresji. Po wywołaniu metody compress() i uncompress() dla długości tablicy bajtów zostaje ustawiona nowa długość, a właściwość position zostaje ustawiona na koniec.

Klasa CompressionAlgorithm definiuje stałe, które można używać w celu określenia algorytmu kompresji. Klasa ByteArray obsługuje algorytmy deflate (tylko w środowisku AIR), zlib i LZMA. Format danych skompresowanych zlib został opisany w dokumencie http://www.ietf.org/rfc/rfc1950.txt . Algorytm LZMA został dodany do programu Flash Player 11.4 i środowiska AIR 3.4. Jest opisany na stronie http://www.7-zip.org/7z.html .

Algorytm kompresji deflate można stosować w wielu formatach kompresji, takich jak zlib, gzip, i niektórych implementacjach formatu ZIP. Algorytm kompresji deflate został opisany w dokumencie http://www.ietf.org/rfc/rfc1951.txt .

W następującym przykładzie jest kompresowany obiekt ByteArray o nazwie bytes za pomocą algorytmu LZMA:

bytes.compress(CompressionAlgorithm.LZMA);

W następującym przykładzie anulowana jest kompresja skompresowanego obiektu ByteArray za pomocą algorytmu deflate:

bytes.uncompress(CompressionAlgorithm.LZMA);

Odczytywanie i zapisywanie obiektów

Metody readObject() i writeObject() odczytują obiekt i zapisują go do obiektu ByteArray zakodowanego w serializowanym formacie AMF (Action Message Format). Format AMF to protokół z informacjami na temat praw własności utworzony przez Adobe i stosowany przez różne klasy ActionScript 3.0, w tym Netstream, NetConnection, NetStream, LocalConnection oraz Shared Objects.

Znacznik jednobajtowy opisuje typ zakodowanych danych występujących po nim. Format AMF korzysta z następujących 13 typów danych:

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

Zakodowane dane występują po typie znacznika, chyba że znacznik przedstawia pojedynczą dopuszczalną wartość, np. null lub true bądź false. W takim wypadku nie są kodowane żadne inne dane.

Istnieją dwie wersje formatu AMF: AMF0 i AMF3. AMF 0 obsługuje wysyłanie złożonych obiektów poprzez odniesienie i umożliwia punktom końcowym przywrócenie relacji pomiędzy obiektami. AMF 3 uzupełnia AMF 0 poprzez przesłanie cech i ciągów jako odniesienie oprócz odniesienia do obiektów oraz poprzez obsługę nowych typów danych wprowadzonych w ActionScript 3.0. Właściwość ByteArray.objectEcoding określa wersję pliku AMF użytego w celu zakodowania danych obiektu. Klasa flash.net.ObjectEncoding określa stałe w celu określenia wersji AMF: ObjectEncoding.AMF0 i ObjectEncoding.AMF3 .

W następującym przykładzie wywołana jest metoda writeObject() w celu zapisania obiektu XML do obiektu ByteArray, który zostaje następnie skompresowany za pomocą algorytmu Deflate i zapisany do pliku order na pulpicie. W przykładzie wykorzystana jest etykieta w celu wyświetlenia komunikatu „Wrote order file to desktop!” w oknie AIR po zakończeniu czynności.

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

Metoda readObject() odczytuje obiekt w serializowanym formacie AMF z obiektu ByteArray i zapisuje go w obiekcie określonego typu. W następującym przykładzie odczytany jest plik order z pulpitu do obiektu ByteArray ( inBytes ), cofnięta zostanie kompresja i wywołana metoda readObject() w celu zapisania pliku w obiekcie XML orderXML . W tym przykładzie wykorzystana jest pętla for each() w celu dodania każdego węzła do obszaru tekstowego do wyświetlenia. W tym przykładzie wyświetlana jest również wartość właściwości objectEncoding razem z nagłówkiem zawartości pliku 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(); 
    } 
}