この例では、種類の異なる複数のファイルを含む単純な .zip ファイルを読み取る方法を示します。その手段として、ここでは各ファイルに関連するデータをメタデータから抽出し、ファイルごとに ByteArray に圧縮解除して、ファイルをデスクトップに書き込んでいます。
.zip ファイルの一般的な構造は、
http://www.pkware.com/documents/casestudies/APPNOTE.TXT
に記載されている PKWARE Inc. の仕様に基づきます。最初にあるのは .zip アーカイブ内の最初のファイルのファイルヘッダーとファイルデータで、その後に、他の各ファイルのファイルヘッダーとファイルデータのペアが続きます。ファイルヘッダーの構造については後で説明します。.zip ファイルには次に、必要に応じてデータ記述子レコードが含まれます(通常は、出力 zip ファイルがディスクに保存されずメモリに作成された場合がこれに当たります)。次に、アーカイブ復号化ヘッダー、アーカイブ追加データレコード、中央ディレクトリ構造、Zip64 中央ディレクトリ終了レコード、Zip64 中央ディレクトリ終了ロケーター、中央ディレクトリ終了レコードなど、いくつかの追加オプションエレメントがあります。
この例のコードは、フォルダーを含まない zip ファイルのみを解析するように作られており、データ記述子レコードは想定していません。最後のファイルデータの後にある情報はすべて無視します。
各ファイルのファイルヘッダーの形式は次のとおりです。
ファイルヘッダーシグネチャ
|
4 バイト
|
必要なバージョン
|
2 バイト
|
汎用ビットフラグ
|
2 バイト
|
圧縮方法
|
2 バイト(8 = DEFLATE、0 = UNCOMPRESSED)
|
最終ファイル更新時刻
|
2 バイト
|
最終ファイル更新日
|
2 バイト
|
crc-32
|
4 バイト
|
圧縮サイズ
|
4 バイト
|
非圧縮サイズ
|
4 バイト
|
ファイル名の長さ
|
2 バイト
|
追加フィールドの長さ
|
2 バイト
|
ファイル名
|
variable
|
追加フィールド
|
variable
|
ファイルヘッダーの後には実際のファイルデータがあり、圧縮方法フラグに応じて、圧縮されている場合とされていない場合があります。フラグの値は、ファイルデータが圧縮されていない場合は 0(ゼロ)、データが DEFLATE アルゴリズムで圧縮されている場合は 8、他の圧縮アルゴリズムの場合は別の値になります。
この例のユーザーインターフェイスは、ラベルとテキスト領域(
taFiles
)で構成されています。アプリケーションは、.zip ファイルで検出したファイルごとに、ファイル名、圧縮サイズおよび非圧縮サイズの情報をテキスト領域に書き込みます。次の MXML ドキュメントは、Flex バージョンのアプリケーションのユーザーインターフェイスの定義です。
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" creationComplete="init();">
<mx:Script>
<![CDATA[
// The application code goes here
]]>
</mx:Script>
<mx:Form>
<mx:FormItem label="Output">
<mx:TextArea id="taFiles" width="320" height="150"/>
</mx:FormItem>
</mx:Form>
</mx:WindowedApplication>
この例のユーザーインターフェイスは、ラベルとテキスト領域(
taFiles
)で構成されています。アプリケーションは、.zip ファイルで検出したファイルごとに、ファイル名、圧縮サイズおよび非圧縮サイズの情報をテキスト領域に書き込みます。次の HTML ページは、アプリケーションのユーザーインターフェイスの定義です。
<html>
<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">
// The application code goes here
</script>
</head>
<body onload="init();">
<div id="taFiles"></div>
</body>
</html>
プログラムの最初で、次のタスクを実行します。
-
ByteArray の
bytes
を定義します。
var bytes = new air.ByteArray();
-
ファイルヘッダーのメタデータを格納する変数を定義します。
// variables for reading fixed portion of file header
var fileName = new String();
var flNameLength;
var xfldLength;
var offset;
var compSize;
var uncompSize;
var compMethod;
var signature;
var output;
-
.zip ファイルを表す File(
zfile
)オブジェクトと FileStream(
zStream
)オブジェクトを定義し、展開するファイルが含まれている .zip ファイルの場所(デスクトップディレクトリの「HelloAIR.zip」という名前のファイル)を指定します。
// File variables for accessing .zip file
var zfile = air.File.desktopDirectory.resolvePath("HelloAIR.zip");
var zStream = new air.FileStream();
Flex の場合、プログラムのコードは
init()
メソッドで開始し、このメソッドは、ルート
mx:WindowedApplication
タグの
creationComplete
ハンドラーとして呼び出されます。
プログラムのコードは
init()
メソッドで開始し、このメソッドは、
body
タグの
onload
イベントハンドラーとして呼び出されます。
function init()
{
プログラムは、.zip ファイルを READ モードで開くことにより開始します。
zStream.open(zfile, air.FileMode.READ);
次に、
bytes
の
endian
プロパティを
LITTLE_ENDIAN
に設定し、数値フィールドのバイト順序は最下位バイトが最初であることを示します。
bytes.endian = air.Endian.LITTLE_ENDIAN;
次に、
while()
ステートメントでループを開始し、ファイルストリーム内の現在位置がファイルのサイズ以上になるまで続けます。
while (zStream.position < zfile.size)
{
ループ内部の最初のステートメントでは、ファイルストリームの最初の 30 バイトを ByteArray の
bytes
に読み取ります。最初の 30 バイトは、最初のファイルヘッダーの固定サイズ部分を構成します。
// read fixed metadata portion of local file header
zStream.readBytes(bytes, 0, 30);
次に、コードは、30 バイトのヘッダーの最初のバイトから整数(
signature
)を読み取ります。ZIP 形式の定義では、すべてのファイルヘッダーのシグネチャは 16 進数値
0x04034b50
と指定されています。シグネチャが異なる場合は、コードが .zip ファイルのファイル部分を超えており、展開するファイルがそれ以上ないことを意味します。その場合は、コードはバイト配列の終端を待つことなく、直ちに
while
ループを終了します。
bytes.position = 0;
signature = bytes.readInt();
// if no longer reading data files, quit
if (signature != 0x04034b50)
{
break;
}
コードの次の部分では、オフセット位置 8 のヘッダーバイトを読み取って、値を変数
compMethod
に格納します。このバイトには、そのファイルの圧縮時に使用された圧縮方法を示す値が含まれます。複数の圧縮方法を使用できますが、実際には、ほとんどすべての .zip ファイルで DEFLATE 圧縮アルゴリズムが使用されます。現在のファイルが DEFLATE 圧縮アルゴリズムで圧縮されている場合は、
compMethod
は 8 です。ファイルが圧縮されていない場合は、
compMethod
は 0 です。
bytes.position = 8;
compMethod = bytes.readByte(); // store compression method (8 == Deflate)
最初の 30 バイトの後にはヘッダーの可変長部分があり、ファイル名と、場合によっては追加フィールドが含まれます。変数
offset
に、この部分のサイズが格納されています。サイズはファイル名の長さと追加フィールドの長さを加えたもので、これらの値はヘッダーのオフセット 26 および 28 から読み取ります。
offset = 0; // stores length of variable portion of metadata
bytes.position = 26; // offset to file name length
flNameLength = bytes.readShort(); // store file name
offset += flNameLength; // add length of file name
bytes.position = 28; // offset to extra field length
xfldLength = bytes.readShort();
offset += xfldLength; // add length of extra field
次に、プログラムは、
offset
変数に格納されているバイト数だけ、ファイルヘッダーの可変長部分を読み取ります。
// read variable length bytes between fixed-length header and compressed file data
zStream.readBytes(bytes, 30, offset);
そして、ヘッダーの可変長部分からファイル名を読み取り、ファイルの圧縮サイズ(zip サイズ)と非圧縮サイズ(元のサイズ)と共にテキスト領域に表示します。
bytes.position = 30;
fileName = bytes.readUTFBytes(flNameLength); // read file name
output += fileName + "<br />"; // write file name to text area
bytes.position = 18;
compSize = bytes.readUnsignedInt(); // store size of compressed portion
output += "\tCompressed size is: " + compSize + '<br />';
bytes.position = 22; // offset to uncompressed size
uncompSize = bytes.readUnsignedInt(); // store uncompressed size
output += "\tUncompressed size is: " + uncompSize + '<br />';
続いて、圧縮サイズで指定されている長さだけファイルストリームからファイルの残りを読み取って
bytes
に格納し、最初の 30 バイトのファイルヘッダーを上書きします。圧縮サイズは、ファイルが圧縮されていない場合でも正確です。その場合の圧縮サイズはファイルの非圧縮サイズと等しくなるからです。
// read compressed file to offset 0 of bytes; for uncompressed files
// the compressed and uncompressed size is the same
if (compSize == 0) continue;
zStream.readBytes(bytes, 0, compSize);
次に、圧縮ファイルの圧縮を解除し、
outfile()
関数を呼び出して出力ファイルストリームに書き込みます。
outfile()
には、ファイル名と、ファイルデータを含むバイト配列を渡します。
if (compMethod == 8) // if file is compressed, uncompress
{
bytes.uncompress(air.CompressionAlgorithm.DEFLATE);
}
outFile(fileName, bytes); // call outFile() to write out the file
右中括弧は、
while
ループの終わり、および
init()
メソッドとアプリケーションコード(
outFile()
メソッド以外)の終わりを示します。実行は
while
ループの先頭に戻り、.zip ファイルの次のバイトの処理を続けます。つまり、別のファイルを展開するか、または最後のファイルの処理が済んだ場合は .zip ファイルの処理を終了します。すべてのファイルの処理が終了したら、
output
変数の内容を
div
エレメントの
taFiles
に書き込み、ファイルの情報を画面に表示します。
} // end of while loop
document.getElementById("taFiles").innerHTML = output;
} // end of init() method
outfile()
関数は、デスクトップ上の出力ファイルを WRITE モードで開き、
filename
パラメーターで指定された名前を設定します。次に、ファイルデータを
data
パラメーターから出力ファイルストリーム(
outStream
)に書き込み、ファイルを閉じます。
function outFile(fileName, data)
{
var outFile = air.File.desktopDirectory; // dest folder is desktop
outFile = outFile.resolvePath(fileName); // name of file to write
var outStream = new air.FileStream();
// open output file stream in WRITE mode
outStream.open(outFile, air.FileMode.WRITE);
// write out the file
outStream.writeBytes(data, 0, data.length);
// close it
outStream.close();
}