Lecture et écriture d’un objet ByteArray

Flash Player 9 et les versions ultérieures, Adobe AIR 1.0 et les versions ultérieures

La classe ByteArray fait partie du package flash.utils. Afin de créer un objet ByteArray dans ActionScript 3.0, importez la classe ByteArray et appelez le constructeur, comme décrit dans l’exemple suivant :

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

Méthodes ByteArray

Tout flux de données significatif est organisé selon un format spécifique que vous pouvez analyser afin d’y trouver les informations souhaitées. Par exemple, un enregistrement dans un fichier d’employé simple, comprendrait très certainement un numéro d’ID, un nom, une adresse, un numéro de téléphone, etc. Un fichier audio MP3 contient une balise ID3 permettant d’identifier le titre, l’auteur, l’album, la date d’édition et le genre du fichier en cours de téléchargement. Le format vous permet de connaître l’ordre dans lequel vous devez vous attendre à recevoir les données du flux de données. Il vous permet de lire le flux d’octets de manière intelligente.

La classe ByteArray comprend plusieurs méthodes destinées à faciliter la lecture depuis un flux de données et l’écriture vers lui. Parmi les méthodes disponibles, on compte celles-ci : readBytes() et writeBytes(), readInt() et writeInt(), readFloat() et writeFloat(), readObject() et writeObject(), et readUTFBytes() et writeUTFBytes(). Grâce à ces méthodes, vous pouvez lire des données provenant du flux de données dans des variables de types de données spécifiques et écrire directement depuis ces types de données vers un flux de données binaire.

Par exemple, le code suivant lit un simple tableau de chaînes et de nombres à virgule flottante, puis écrit chaque élément vers un objet ByteArray. La structure du tableau permet au code d’appeler les méthodes ByteArray appropriées (writeUTFBytes() et writeFloat()) afin d’écrire les données. Le modèle de données répétitif permet de lire le tableau au moyen d’une boucle.

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

Propriété position

La propriété position stocke la position actuelle du pointeur qui indexe la classe ByteArray au cours de la lecture ou de l’écriture. La valeur initiale de la propriété position est égale à 0 (zéro) comme l’illustre l’exemple de code suivant :

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

Lors de la lecture à partir d’une classe ByteArray ou de l’écriture vers elle, la méthode utilisée met à jour la propriété position de sorte qu’elle pointe vers l’emplacement suivant immédiatement le dernier octet lu ou écrit. Par exemple, le code suivant écrit une chaîne dans une classe ByteArray. Ensuite, la propriété position pointe vers l’octet qui suit immédiatement la chaîne dans 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 

De la même manière, une opération de lecture incrémente la propriété position en fonction du nombre d’octets lus.

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! 

Vous noterez qu’il est possible de définir la propriété position sur un emplacement spécifique dans la classe ByteArray afin de lire ou d’écrire selon ce décalage.

Propriétés bytesAvailable et length

Les propriétés length et bytesAvailable vous indiquent la longueur d’une classe ByteArray et le nombre d’octets restants entre la position active et la fin. L’exemple suivant illustre des modes d’utilisation de ces propriétés. L’exemple écrit une chaîne de texte dans la classe ByteArray, puis lit la classe ByteArray octet par octet jusqu’à la détection du caractère « a » ou l’arrivée au terme (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 
} 

Propriété endian

Les ordinateurs ne stockent pas tous de la même manière les nombres à plusieurs octets, autrement dit, les nombres nécessitant plus d’un octet de mémoire de stockage. Un nombre entier, par exemple, peut prendre 4 octets (ou 32 bits) de mémoire. Certains ordinateurs commencent par stocker l’octet le plus important du nombre (dans l’adresse mémoire de plus bas niveau) tandis que d’autres stockent d’abord l’octet le moins important. Cet attribut d’ordinateur (ou ordre d’octets) est connu comme étant soit gros-boutiste (l’octet le plus important en premier lieu) soit petit-boutiste (l’octet le moins important en premier lieu). Par exemple, le nombre 0x31323334 serait stocké de la manière suivante pour les ordres d’octets gros-boutiste et petit-boutiste, où a0 représente l’adresse mémoire de plus bas niveau des 4 octets et a3 correspond à celle de plus haut niveau :

Gros-boutiste

Gros-boutiste

Gros-boutiste

Gros-boutiste

a0

a1

a2

a3

31

32

33

34

Petit-boutiste

Petit-boutiste

Petit-boutiste

Petit-boutiste

a0

a1

a2

a3

34

33

32

31

La propriété endian de la classe ByteArray vous permet de désigner cet ordre d’octets pour les nombres à plusieurs octets que vous traitez. Les valeurs admises pour cette propriété sont bigEndian (gros-boutiste) et littleEndian (petit-boutiste). La classe Endian définit les constantes BIG_ENDIAN et LITTLE_ENDIAN pour configurer la propriété endian à l’aide de ces chaînes.

Méthodes compress() et uncompress()

La méthode compress() vous permet de compresser une classe ByteArray conformément à un algorithme de compression spécifié sous forme de paramètre. La méthode uncompress() vous permet de décompresser un tableau ByteArray compressé conformément à un algorithme de compression. Une fois que vous avez appelé compress() et uncompress(), la longueur du tableau d’octets est fixée selon la nouvelle longueur et la propriété position est configurée sur la fin.

La classe CompressionAlgorithm définit des constantes pouvant servir à spécifier l'algorithme de compression. La classe ByteArray prend en charge les algorithmes deflate (AIR uniquement), zlib, et lzma. Le format de données compressé zlib est décrit à l’adresse http://www.ietf.org/rfc/rfc1950.txt. L’algorithme lzma a été ajouté à Flash Player 11.4 et AIR 3.4. Il est décrit sur la page suivante : http://www.7-zip.org/7z.html.

L’algorithme de compression deflate est utilisé dans plusieurs formats de compression tels que zlib, gzip et certaines implémentations zip. L’algorithme de compression deflate est décrit à l’adresse http://www.ietf.org/rfc/rfc1951.txt.

Dans l’exemple suivant, un tableau ByteArray appelé bytes est compressé à l’aide de l’algorithme Izma :

bytes.compress(CompressionAlgorithm.LZMA);

Dans l’exemple suivant, un tableau ByteArray compressé est décompressé à l’aide de l’algorithme deflate :

bytes.uncompress(CompressionAlgorithm.LZMA);

Lecture et écriture d’objets

Les méthodes readObject() et writeObject() permettent de lire un objet à partir d’une classe ByteArray et d’en écrire un dans une telle classe. Cet objet est codé au format AMF sérialisé. AMF est un protocole de message propriétaire créé par Adobe et utilisé par différentes classes ActionScript 3.0, notamment les objets Netstream, NetConnection, NetStream, LocalConnection et Shared.

Un marqueur de type « un octet » décrit le type des données codées qui suivent. Le format AMF fait appel aux 13 types de données suivants :

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

Les données codées suivent le marqueur de type à moins que ce dernier ne représente qu’une seule valeur possible (nulle, vraie ou fausse, par exemple), auquel cas aucun autre élément n’est codé.

Il existe deux versions du format AMF : AMF0 et AMF3. AMF 0 prend en charge l’envoi d’objets complexes par référence et permet aux points de fin de restaurer les relations entre objets. AMF 3 améliore le format AMF 0 en envoyant les traits et chaînes de l’objet par référence, en plus des références d’objet, et en prenant en charge de nouveaux types de données introduits dans la version 3.0 d’ActionScript. La propriété ByteArray.objectEcoding spécifie la version du format AMF utilisée pour coder les données d’objet. La classe flash.net.ObjectEncoding définit des constantes permettant de préciser la version du format AMF : ObjectEncoding.AMF0 et ObjectEncoding.AMF3.

Dans l’exemple suivant, writeObject() est appelé pour écrire un objet XML dans une classe ByteArray, qu’il compresse ensuite à l’aide de l’algorithme Deflate et écrit dans le fichier order situé dans le poste de travail. Dans cet exemple, une étiquette affiche le message « Wrote order file to desktop! » (Fichier d’ordre écrit dans le poste de travail) dans la fenêtre d’AIR lorsque l’opération est terminée.

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

La méthode readObject() lit un objet au format AMF sérialisé à partir d’une classe ByteArray et le stocke dans un objet du type spécifié. Dans l’exemple suivant, le fichier order est lu à partir du poste de travail dans une classe ByteArray (inBytes), puis il est décompressé. readObject() est ensuite appelé pour stocker le fichier dans l’objet XML orderXML. Dans cet exemple, une construction de boucle for each() est utilisée pour ajouter chaque nœud à une zone de texte à des fins d’affichage. L’exemple présente également la valeur de la propriété objectEncoding accompagnée d’un en-tête pour le contenu du fichier 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(); 
    } 
}