ByteArray の例:.zip ファイルの読み取りAdobe AIR 1.0 およびそれ以降 この例では、種類の異なる複数のファイルを含む単純な .zip ファイルを読み取る方法を示します。その手段として、ここでは各ファイルに関連するデータをメタデータから抽出し、ファイルごとに ByteArray に圧縮解除して、ファイルをデスクトップに書き込んでいます。 .zip ファイルの一般的な構造は、http://www.pkware.com/documents/casestudies/APPNOTE.TXT に記載されている PKWARE Inc. の仕様に基づきます。最初にあるのは .zip アーカイブ内の最初のファイルのファイルヘッダーとファイルデータで、その後に、他の各ファイルのファイルヘッダーとファイルデータのペアが続きます。ファイルヘッダーの構造については後で説明します。.zip ファイルには次に、必要に応じてデータ記述子レコードが含まれます(通常は、出力 zip ファイルがディスクに保存されずメモリに作成された場合がこれに当たります)。次に、アーカイブ復号化ヘッダー、アーカイブ追加データレコード、中央ディレクトリ構造、Zip64 中央ディレクトリ終了レコード、Zip64 中央ディレクトリ終了ロケーター、中央ディレクトリ終了レコードなど、いくつかの追加オプションエレメントがあります。 この例のコードは、フォルダーを含まない zip ファイルのみを解析するように作られており、データ記述子レコードは想定していません。最後のファイルデータの後にある情報はすべて無視します。 各ファイルのファイルヘッダーの形式は次のとおりです。
ファイルヘッダーの後には実際のファイルデータがあり、圧縮方法フラグに応じて、圧縮されている場合とされていない場合があります。フラグの値は、ファイルデータが圧縮されていない場合は 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> プログラムの最初で、次のタスクを実行します。
Flex の場合、プログラムのコードは init() メソッドで開始し、このメソッドは、ルート mx:WindowedApplication タグの creationComplete ハンドラーとして呼び出されます。 // for Flex private function init():void { プログラムは、.zip ファイルを READ モードで開くことにより開始します。 zStream.open(zfile, FileMode.READ); 次に、bytes の endian プロパティを LITTLE_ENDIAN に設定し、数値フィールドのバイト順序は最下位バイトが最初であることを示します。 bytes.endian = 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 サイズ)と非圧縮サイズ(元のサイズ)と共にテキスト領域に表示します。 // Flash version bytes.position = 30; fileName = bytes.readUTFBytes(flNameLength); // read file name taFiles.appendText(fileName + "\n"); // write file name to text area bytes.position = 18; compSize = bytes.readUnsignedInt(); // store size of compressed portion taFiles.appendText("\tCompressed size is: " + compSize + '\n'); bytes.position = 22; // offset to uncompressed size uncompSize = bytes.readUnsignedInt(); // store uncompressed size taFiles.appendText("\tUncompressed size is: " + uncompSize + '\n'); // Flex version bytes.position = 30; fileName = bytes.readUTFBytes(flNameLength); // read file name taFiles.text += fileName + "\n"; // write file name to text area bytes.position = 18; compSize = bytes.readUnsignedInt(); // store size of compressed portion taFiles.text += "\tCompressed size is: " + compSize + '\n'; bytes.position = 22; // offset to uncompressed size uncompSize = bytes.readUnsignedInt(); // store uncompressed size taFiles.text += "\tUncompressed size is: " + uncompSize + '\n'; 続いて、圧縮サイズで指定されている長さだけファイルストリームからファイルの残りを読み取って bytes に格納し、最初の 30 バイトのファイルヘッダーを上書きします。圧縮サイズは、ファイルが圧縮されていない場合でも正確です。その場合の圧縮サイズはファイルの非圧縮サイズと等しくなるからです。 // read compressed file to offset 0 of bytes; for uncompressed files // the compressed and uncompressed size is the same zStream.readBytes(bytes, 0, compSize); 次に、圧縮ファイルの圧縮を解除し、outfile() 関数を呼び出して出力ファイルストリームに書き込みます。outfile() には、ファイル名と、ファイルデータを含むバイト配列を渡します。 if (compMethod == 8) // if file is compressed, uncompress { bytes.uncompress(CompressionAlgorithm.DEFLATE); } outFile(fileName, bytes); // call outFile() to write out the file 右中括弧は、while ループの終わり、および init() メソッドと Flex アプリケーションコード(outFile() メソッド以外)の終わりを示します。実行は while ループの先頭に戻り、.zip ファイルの次のバイトの処理を続けます。つまり、別のファイルを展開するか、または最後のファイルの処理が済んだ場合は .zip ファイルの処理を終了します。 } // end of while loop } // for Flex version, end of init() method and application outfile() 関数は、デスクトップ上の出力ファイルを WRITE モードで開き、filename パラメーターで指定された名前を設定します。次に、ファイルデータを data パラメーターから出力ファイルストリーム(outStream)に書き込み、ファイルを閉じます。 // Flash version function outFile(fileName:String, data:ByteArray):void { var outFile:File = File.desktopDirectory; // destination 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(); } private function outFile(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(); } |
![]() |