Reading and writing a ByteArray
Flash Player 9 and later, Adobe AIR 1.0 and
later
The ByteArray
class is part of the flash.utils package; you can also use the alias
air.ByteArray
to
refer to the ByteArray class if your code includes the AIRAliases.js
file. To create a ByteArray, invoke the ByteArray constructor as
shown in the following example:
var stream = new air.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.
// define the grocery list Array
var groceries = ["milk", 4.50, "soup", 1.79, "eggs", 3.19, "bread" , 2.35]
// define the ByteArray
var bytes = new air.ByteArray();
// for each item in the array
for (i = 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
air.trace("bytes.position is: " + bytes.position); //display the position in ByteArray
}
air.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 = new air.ByteArray();
air.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 = new air.ByteArray();
air.trace("bytes.position is initially: " + bytes.position); // 0
bytes.writeUTFBytes("Hello World!");
air.trace("bytes.position is now: " + bytes.position); // 12
Likewise, a read operation increments the position property by
the number of bytes read.
var bytes = new air.ByteArray();
air.trace("bytes.position is initially: " + bytes.position); // 0
bytes.writeUTFBytes("Hello World!");
air.trace("bytes.position is now: " + bytes.position); // 12
bytes.position = 0;
air.trace("The first 6 bytes are: " + (bytes.readUTFBytes(6))); //Hello
air.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 = new air.ByteArray();
var text = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Vivamus etc.";
bytes.writeUTFBytes(text); // write the text to the ByteArray
air.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) {
air.trace("Found the letter a; position is: " + bytes.position); // 23
air.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 (AIR) defines constants that you
can use to specify the compression algorithm. The ByteArray class
supports both the deflate (AIR-only) and zlib algorithms. The deflate
compression algorithm is used in several compression formats, such
as zlib, gzip, and some zip implementations. The zlib compressed
data format is described at
http://www.ietf.org/rfc/rfc1950.txt
and
the deflate compression algorithm is described at
http://www.ietf.org/rfc/rfc1951.txt
.
The following example compresses a ByteArray called
bytes
using
the deflate algorithm:
bytes.compress(air.CompressionAlgorithm.DEFLATE);
The following example uncompresses a compressed ByteArray using
the deflate algorithm:
bytes.uncompress(CompressionAlgorithm.DEFLATE);
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 writes to the
order
file
on the desktop. The example displays the message “Wrote order file
to desktop!” in the AIR window when it is finished.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">
<head>
<style type="text/css">
#taFiles
{
border: 1px solid black;
font-family: Courier, monospace;
white-space: pre;
width: 95%;
height: 95%;
overflow-y: scroll;
}
</style>
<script type="text/javascript" src="AIRAliases.js" ></script>
<script type="text/javascript">
//define ByteArray
var inBytes = new air.ByteArray();
//add objectEncoding value and file heading to output text
var output = "Object encoding is: " + inBytes.objectEncoding + "\n\n" + "order file: \n\n";
function init() {
readFile("order", inBytes);
inBytes.position = 0; //reset position to beginning
// read XML from ByteArray
var orderXML = inBytes.readObject();
// convert to XML Document object
var myXML = (new DOMParser()).parseFromString(orderXML, "text/xml");
document.write(myXML.getElementsByTagName("menuName")[0].childNodes[0].nodeValue + ": ");
document.write(myXML.getElementsByTagName("price")[0].childNodes[0].nodeValue + "<br/>"); // burger: 3.95
document.write(myXML.getElementsByTagName("menuName")[1].childNodes[0].nodeValue + ": ");
document.write(myXML.getElementsByTagName("price")[1].childNodes[0].nodeValue + "<br/>"); // fries: 1.45
} // end of init()
// read specified file into byte array
function readFile(fileName, data) {
var inFile = air.File.desktopDirectory; // source folder is desktop
inFile = inFile.resolvePath(fileName); // name of file to read
var inStream = new air.FileStream();
inStream.open(inFile, air.FileMode.READ);
inStream.readBytes(data, 0, data.length);
inStream.close();
}
</script>
</head>
<body onload = "init();">
<div id="taFiles"></div>
</body>
</html>
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
)
and calls
readObject()
to store it in
orderXML
,
which it then converts to an XML object document,
myXML
,
and displays the values of two item and price elements. The example
also displays the value of the
objectEncoding
property
along with a header for the contents of the
order
file.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<style type="text/css">
#taFiles
{
border: 1px solid black;
font-family: Courier, monospace;
white-space: pre;
width: 95%;
height: 95%;
overflow-y: scroll;
}
</style>
<script type="text/javascript" src="AIRAliases.js" ></script>
<script type="text/javascript">
//define ByteArray
var inBytes = new air.ByteArray();
//add objectEncoding value and file heading to output text
var output = "Object encoding is: " + inBytes.objectEncoding + "<br/><br/>" + "order file items:" + "<br/><br/>";
function init() {
readFile("order", inBytes);
inBytes.position = 0; //reset position to beginning
// read XML from ByteArray
var orderXML = inBytes.readObject();
// convert to XML Document object
var myXML = (new DOMParser()).parseFromString(orderXML, "text/xml");
document.write(output);
document.write(myXML.getElementsByTagName("menuName")[0].childNodes[0].nodeValue + ": ");
document.write(myXML.getElementsByTagName("price")[0].childNodes[0].nodeValue + "<br/>"); // burger: 3.95
document.write(myXML.getElementsByTagName("menuName")[1].childNodes[0].nodeValue + ": ");
document.write(myXML.getElementsByTagName("price")[1].childNodes[0].nodeValue + "<br/>"); // fries: 1.45
} // end of init()
// read specified file into byte array
function readFile(fileName, data) {
var inFile = air.File.desktopDirectory; // source folder is desktop
inFile = inFile.resolvePath(fileName); // name of file to read
var inStream = new air.FileStream();
inStream.open(inFile, air.FileMode.READ);
inStream.readBytes(data, 0, data.length);
inStream.close();
}
</script>
</head>
<body onload = "init();">
<div id="taFiles"></div>
</body>
</html>
|
|
|
|
|