Reading and writing a ByteArray
The ByteArray class is part of the flash.utils package. To create a ByteArray object in ActionScript 3.0, import the ByteArray class and invoke the constructor, as shown in the following example:
import flash.utils.ByteArray;
var stream:ByteArray = new ByteArray();
ByteArray methods
Any meaningful data stream is organized into a format that you can analyze to find the information that you want. A record in a simple employee file, for example, would probably include an ID number, a name, an address, a phone number, and so on. An MP3 audio file contains an ID3 tag that identifies the title, author, album, publishing date, and genre of the file that's being downloaded. The format allows you to know the order in which to expect the data on the data stream. It allows you to read the byte stream intelligently.
The ByteArray class includes several methods that make it easier to read from
and write to a data stream. Some of these methods include readBytes()
and
writeBytes()
, readInt()
and writeInt()
, readFloat()
and writeFloat()
,
readObject()
and writeObject()
, and readUTFBytes()
and writeUTFBytes()
.
These methods enable you to read data from the data stream into variables of
specific data types and write from specific data types directly to the binary
data stream.
For example, the following code reads a simple array of strings and
floating-point numbers and writes each element to a ByteArray. The organization
of the array allows the code to call the appropriate ByteArray methods (
writeUTFBytes()
and writeFloat()
) to write the data. The repeating data
pattern makes it possible to read the array with a loop.
// 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
The position property
The position property stores the current position of the pointer that indexes the ByteArray during reading or writing. The initial value of the position property is 0 (zero) as shown in the following code:
var bytes:ByteArray = new ByteArray();
trace("bytes.position is initially: " + bytes.position); // 0
When you read from or write to a ByteArray, the method that you use updates the position property to point to the location immediately following the last byte that was read or written. For example, the following code writes a string to a ByteArray and afterward the position property points to the byte immediately following the string in the 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
Likewise, a read operation increments the position property by the number of bytes read.
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!
Notice that you can set the position property to a specific location in the ByteArray to read or write at that offset.
The bytesAvailable and length properties
The length
and bytesAvailable
properties tell you how long a ByteArray is
and how many bytes remain in it from the current position to the end. The
following example illustrates how you can use these properties. The example
writes a String of text to the ByteArray and then reads the ByteArray one byte
at a time until it encounters either the character "a" or the end (
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
}
The endian property
Computers can differ in how they store multibyte numbers, that is, numbers that require more than 1 byte of memory to store them. An integer, for example, can take 4 bytes, or 32 bits, of memory. Some computers store the most significant byte of the number first, in the lowest memory address, and others store the least significant byte first. This attribute of a computer, or of byte ordering, is referred to as being either big endian (most significant byte first) or little endian (least significant byte first). For example, the number 0x31323334 would be stored as follows for big endian and little endian byte ordering, where a0 represents the lowest memory address of the 4 bytes and a3 represents the highest:
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 |
The endian
property of the ByteArray class allows you to denote this byte
order for multibyte numbers that you are processing. The acceptable values for
this property are either "bigEndian"
or "littleEndian"
and the Endian class
defines the constants BIG_ENDIAN
and LITTLE_ENDIAN
for setting the endian
property with these strings.
The compress() and uncompress() methods
The compress()
method allows you to compress a ByteArray in accordance with a
compression algorithm that you specify as a parameter. The uncompress()
method
allows you to uncompress a compressed ByteArray in accordance with a compression
algorithm. After calling compress()
and uncompress()
, the length of the byte
array is set to the new length and the position property is set to the end.
The CompressionAlgorithm class defines constants that you can use to specify the compression algorithm. The ByteArray class supports the deflate (AIR-only), zlib, and lzma algorithms. The zlib compressed data format is described at http://www.ietf.org/rfc/rfc1950.txt. The lzma algorithm was added for Flash Player 11.4 and AIR 3.4. It is described at http://www.7-zip.org/7z.html.
The deflate compression algorithm is used in several compression formats, such as zlib, gzip, and some zip implementations. The deflate compression algorithm is described at http://www.ietf.org/rfc/rfc1951.txt.
The following example compresses a ByteArray called bytes
using the lzma
algorithm:
bytes.compress(CompressionAlgorithm.LZMA);
The following example uncompresses a compressed ByteArray using the deflate algorithm:
bytes.uncompress(CompressionAlgorithm.LZMA);
Reading and writing objects
The readObject()
and writeObject()
methods read an object from and write an
object to a ByteArray, encoded in serialized Action Message Format (AMF). AMF is
a proprietary message protocol created by Adobe and used by various ActionScript
3.0 classes, including Netstream, NetConnection, NetStream, LocalConnection, and
Shared Objects.
A one-byte type marker describes the type of the encoded data that follows. AMF uses the following 13 data types:
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
The encoded data follows the type marker unless the marker represents a single possible value, such as null or true or false, in which case nothing else is encoded.
There are two versions of AMF: AMF0 and AMF3. AMF 0 supports sending complex
objects by reference and allows endpoints to restore object relationships. AMF 3
improves AMF 0 by sending object traits and strings by reference, in addition to
object references, and by supporting new data types that were introduced in
ActionScript 3.0. The ByteArray.objectEcoding
property specifies the version
of AMF that is used to encode the object data. The flash.net.ObjectEncoding
class defines constants for specifying the AMF version: ObjectEncoding.AMF0
and ObjectEncoding.AMF3
.
The following example calls writeObject()
to write an XML object to a
ByteArray, which it then compresses using the Deflate algorithm and writes to
the order
file on the desktop. The example uses a label to display the message
"Wrote order file to desktop!" in the AIR window when it is finished.
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();
}
}
The readObject()
method reads an object in serialized AMF from a ByteArray and
stores it in an object of the specified type. The following example reads the
order
file from the desktop into a ByteArray ( inBytes
), uncompresses it,
and calls readObject()
to store it in the XML object orderXML
. The example
uses a for each()
loop construct to add each node to a text area for display.
The example also displays the value of the objectEncoding
property along with
a header for the contents of the order
file.
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();
}
}