讀取與寫入 ByteArray

Flash Player 9 以及更新的版本,Adobe AIR 1.0 以及更新的版本

ByteArray 類別是 flash.utils 套件的一份子。若要在 ActionScript 3.0 中建立 ByteArray 物件,請匯入 ByteArray 類別並叫用其建構函式,如下列範例所示:

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

ByteArray 方法

任何有意義的資料串流都會組織成可供您分析以找出所需資訊的特定格式。例如,一份簡單的員工資料檔可能含有身分證字號、姓名、地址和電話號碼等各項記錄。MP3 音效檔則含有 ID3 標籤,以識別曲名、作者、專輯、出版年份和下載檔案的歌曲類型。這些格式可讓您瞭解各項資料在資料串流中的預期順序,進而能夠明智地讀取位元組串流。

ByteArray 類別包含數種方法,使得資料串流的讀取與寫入更為容易。其中一些方法包括 readBytes() writeBytes() readInt() writeInt() readFloat() writeFloat() readObject() writeObject() ,以及 readUTFBytes() writeUTFBytes() 。這些方法讓您得以從資料串流將資料讀入特定資料類型的變數中,並可直接將特定資料類型寫入二進位資料串流。

例如,下列程式碼會讀取含有字串和浮點數的簡單陣列,並將每個元素寫入 ByteArray。陣列的組織架構讓程式碼能藉由呼叫適當的 ByteArray 方法 ( writeUTFBytes() writeFloat() ) 以寫入資料。重複性的資料模式則會使程式碼得以利用迴圈讀取陣列。

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

position 屬性

position 屬性儲存了在讀取或寫入期間,對 ByteArray 編列索引的指標目前位置。position 屬性的初始值為 0 (零),如下列程式碼所示:

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

當您讀取或寫入 ByteArray 時,所用的方法會更新 position 屬性,以指向最後讀取或寫入的位元組的下一個位置。例如,下列程式碼會將某字串寫入 ByteArray,之後,position 屬性即指向 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 

同樣地,讀取作業會以讀取的位元組數目來遞增 position 屬性。

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! 

請注意,您可將 position 屬性設為 ByteArray 中的任何特定位置,以便讀取或寫入該偏移位置。

bytesAvailable 和 length 屬性

length 屬性會指出 ByteArray 的長度, bytesAvailable 屬性則會指出從目前位置到陣列結尾的剩餘位元組數目。下列範例會說明如何使用這些屬性。此範例會先將文字字串寫入 ByteArray,接著從 ByteArray 一次讀取一個位元組,直到發現字元 "a" 或抵達陣列結尾 ( 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 
} 

endian 屬性

多位元組數字 (亦即需要 1 個位元組以上的記憶體才夠儲存的數字) 儲存在電腦上的方式可能各有不同。例如,整數需要 4 個位元組的記憶體,也就是 32 個位元。某些電腦會將最高位元組數字儲存在前,置於最低記憶體位址空間,有些則是將最低位元組儲存在前。電腦 (或位元組定序) 的這項特質稱為 big endian (最高位元組在前) 或 little endian (最低位元組在前)。例如,數字 0x31323334 將儲存成如下所示的 big endian 和 little endian 位元組順序,其中 a0 代表最低記憶體位址空間的 4 個位元組,而 a3 則代表最高位址空間:

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

ByteArray 類別的 endian 屬性可讓您為目前正在處理的多位元組數字標註這種位元組順序。此屬性可接受的值為 "bigEndian" "littleEndian" ,而 Endian 類別更定義了 BIG_ENDIAN LITTLE_ENDIAN 常數,便於您將 endian 屬性設為這些字串。

compress() 和 uncompress() 方法

compress() 方法可讓您指定壓縮演算法做為參數,根據此參數來壓縮 ByteArray。 uncompress() 方法則可讓您指定壓縮演算法,根據此參數來將壓縮過的 ByteArray 進行解壓縮。呼叫 compress() uncompress() 之後,位元組陣列的長度將設定為新長度,而 position 屬性會設定為陣列結尾。

CompressionAlgorithm 類別定義了可供您指定壓縮演算法的常數。ByteArray 類別支援 deflate (僅限 AIR)、zlib 和 lzma 演算法。zlib 壓縮資料格式於 http://www.ietf.org/rfc/rfc1950.txt 中說明。lzma 演算法是 Flash Player 11.4 和 AIR 3.4 新增的演算法,於 http://www.7-zip.org/7z.html 中說明。

許多壓縮格式都使用 deflate 壓縮演算法,如 zlib、gzip 及一些 zip 實作。deflate 壓縮演算法於 http://www.ietf.org/rfc/rfc1951.txt 中說明。

下列範例會使用 lzma 演算法來壓縮名為 bytes 的 ByteArray 陣列:

bytes.compress(CompressionAlgorithm.LZMA);

下列範例會使用 deflate 演算法,將壓縮過的 ByteArray 進行解壓縮:

bytes.uncompress(CompressionAlgorithm.LZMA);

讀取與寫入物件

readObject() 方法會從 ByteArray 讀取以 Action Message Format (AMF) 序列化格式進行編碼的物件,而 writeObject() 方法則可寫入此一編碼格式的物件。AMF 是由 Adobe 建立並已取得專利的訊息通訊協定,廣為各種 ActionScript 3.0 類別所採用,包括 Netstream、NetConnection、NetStream、LocalConnection 和共享物件。

單一位元組的類型標記描述了接續在其後的編碼資料類型。AMF 使用下列 13 種資料類型:

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

編碼資料始終會接續在類型標記之後,除非標記代表單一可能值如 null、true 或 false,此時只有標記會進行編碼。

AMF 有兩個版本:AMF0 和 AMF3。AMF 0 支援以傳址方式傳送複雜的物件,而且允許端點還原物件關係。AMF 3 不但傳送物件參考,更以傳址方式傳送物件特性和字串,並支援 ActionScript 3.0 新引進的資料類型,藉此改良 AMF 0。 ByteArray.objectEcoding 屬性會指定物件資料進行編碼所用的 AMF 版本。flash.net.ObjectEncoding 類別定義了可供您指定 AMF 版本的常數: ObjectEncoding.AMF0 ObjectEncoding.AMF3

下列範例會呼叫 writeObject() 將 XML 物件寫入 ByteArray,接著使用 Deflate 演算法予以壓縮後寫入位於桌面的 order 檔案。此範例會在 AIR 視窗中利用標籤,於作業完成時顯示「Wrote order file to desktop!」訊息。

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

readObject() 方法會從 ByteArray 讀取 AMF 序列化格式的物件,然後儲存成指定類型的物件。下列範例會先將桌面上的 order 檔案讀入 ByteArray ( inBytes ) 並予以解壓縮,然後呼叫 readObject() 將之儲存成 XML 物件 orderXML 。此範例會使用 for each() 迴圈建構,將每個節點加入至文字區域以供顯示。另外,還會顯示 objectEncoding 屬性的值,以及 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(); 
    } 
}