读取并写入 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 方法

任何有意义的数据流均将以某种格式组织起来,以便于您分析并找到所需信息。例如,简单的员工文件中的记录可能包括 ID 号、姓名、地址、电话号码等等。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 bytesAvailable 属性分别指示 ByteArray 的长度为多少以及从当前位置到结尾处还剩多少字节。以下示例说明了您可以通过什么方式使用这些属性。该示例将一个文本字符串写入 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 算法。 http://www.ietf.org/rfc/rfc1950.txt 中介绍了 zlib 压缩的数据格式。lzma 算法是为 Flash Player 11.4 和 AIR 3.4 添加的。 http://www.7-zip.org/7z.html 中介绍了该算法。

这种 deflate 压缩算法用于多种压缩格式,如 zlib、gzip 及一些 zip 实现等。 http://www.ietf.org/rfc/rfc1951.txt 中介绍了 deflate 压缩算法。

以下示例使用 lzma 算法对称为 bytes 的 ByteArray 进行压缩:

bytes.compress(CompressionAlgorithm.LZMA);

以下示例使用 deflate 算法对压缩的 ByteArray 进行解压缩:

bytes.uncompress(CompressionAlgorithm.LZMA);

读取和写入对象

readObject() writeObject() 方法可从 ByteArray 中读取并向其写入以序列化 Action Message Format (AMF) 格式编码的对象。AMF 是 Adobe 创建并由各种 ActionScript 3.0 类使用的专有消息协议,这些类包括 Netstream、NetConnection、NetStream、LocalConnection 和 SharedObject。

单字节类型标记说明了编码数据遵循的类型。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 通过以下方式对 AMF 0 进行了改进:通过引用发送对象 traits 和字符串,除对象引用外,还支持 ActionScript 3.0 中引入的新的数据类型。 ByteArray.objectEcoding 属性指定了用于对对象数据进行编码的 AMF 版本。flash.net.ObjectEncoding 类定义了用于指定 AMF 版本的常量: ObjectEncoding.AMF0 ObjectEncoding.AMF3

以下示例调用 writeObject() 将 XML 对象写入 ByteArray 中,随后使用 Deflate 算法压缩 ByteArray 并将其写入桌面上的 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(); 
    } 
}