| Apache | Jakarta | POI |
POIFSファイルシステム内部POIFSファイルシステム内部はじめにPOIFSファイルシステムは、本質的に、Java互換プラットフォームのネイティブファイルシステムに格納される通常のファイルです。典型的には、それらは名前の末尾4文字の拡張子によって、どのような種類のデータが含まれているかが特定されます。例えば、".xls"で終わるファイルは、おそらくスプレッドシートのデータを含むでしょうし、".doc"で終わるファイルは、おそらくワード処理のドキュメントを含む事でしょう。POIFSファイルシステムは、"file system"と呼ばれます。というのも、古典的なファイルシステムに似た様式の、複数の埋め込みファイル群だからです。機能面での方針に従うと、これらをPOIFSアーカイブ、と呼ぶほうがより正確でしょう。この文書の残りの部分では、POIFSが含む"files"との混同を避けるため、ファイルシステムと言及する事とします。 POIFSファイルシステムは、良く知られたソフトウェア会社の有名なオフィス内の生産性を高めるスイートやプログラムによって使われるドキュメント形式と互換性があり、互換性のあるデータを出力します。 POIFSファイルシステムは、圧縮機能・暗号化機能、その他値打ちのある機能を提供していないため、これらのプログラムとの相互実行可能性を必要としないなら良い選択とは言えないでしょう。 POIFSファイルシステムは、ドキュメント自体をエンコードしません。例えば、ワード処理のファイル(拡張子は".doc")をお持ちならば、そのファイルシステム内で圧縮されたドキュメントと共に、POIFSファイルシステムを実際に持っていることになります。 文書規則この文書は、Java言語仕様書(http://java.sun.comで手に入ります)で記述される数値型を利用します。簡単に言えば:
Java言語仕様書は、これよりも多くの型について詳細に記述していますが、この文書では言及しないこととします。 この文書で、"endian conversion"(エンディアン間変換)を言及する場合、格納された数値のbyte order(順番)の事を指します。リトルエンディアンでの数値は、意味のあるbyteの小さいほうから先に格納されています。例えば、short型を適切に読み込むには 2つのbyteを読み込み、1つ目のバイトとの間にor操作を施す前に2つ目のbyteに8bit左シフトをかけます。以下のコードは、今の方法を示しています:
public int getShort (byte[] rec)
{
return ((rec[1] << 8) | (rec[0] & 0x00ff));
}
ファイルシステム早わかりこのセクションは、POIFSファイルシステムの「早わかり」(攻略法?)であり、それがどのように構成されているか、を示すものです。とはいえ、簡潔な説明をする事を意図しているわけではなく、寧ろ全般的な構造の「俯瞰図」を示し、どのようにそれが解釈されるかを示す事を意図して書かれています。 POIFSファイルシステムは、ヘッダで始まります。このヘッダは、機能別にファイル内の位置を特定し、確かにファイルがPOIFSファイルシステムであるという確認を行うためのものです。 ヘッダの始めの64bitは、魔法数(マジックナンバー)と呼ばれる識別子で構成されています。この識別子は、クライアントソフトに対して、確かにPOIFSファイルシステムである、と言う事を通知し、そのように対処するよう求めるためにあるものです。これは、確実にPOIFSファイルシステムであり他の形式ではない、と言う事を保証する"sanity check"です。ヘッダには、ブロックナンバーの配列も含まれています。これらのブロックナンバーは、ファイル内のブロックの数を指し示すものです。これらのブロックが互いに読み込まれると、それらは"ブロック・アロケーション・テーブル"(BAT)を形成します。ヘッダには、「ルートエレメント」としても知られるプロパティテーブル内の最初の要素へのポインタや、小さなBAT(SBAT)へのポインタも含まれています。 ブロック・アロケーション・テーブル(BAT)は、プロパティテーブルに加え、ファイルシステム内のどのブロックがどのファイルに所属しているかを指定するものです。ヘッダ・ブロックの後は、ファイルシステムは等しいサイズのデータ(0からファイルシステム内にいくらでも多くの数の)ブロックに分けられています。ファイルシステム内の各々のファイルでは、それぞれのプロパティテーブル内のエントリにブロックの配列内の最初のブロックのインデックスが含まれています。ブロックの配列内の各々のブロックのインデックスは、BATの中のインデックスでもあり、BAT内のインデックスにあるnteger値は、配列内の次のブロックのインデックスを与えます。(つまり、次のBAT値のインデックス)"ファイルの終わり(EOF)"を示す特別な値がBAT内に格納されています。 プロパティテーブルは本質的にファイルシステム用のディレクトリ格納庫です。ファイルやディレクトリの名前、ファイルシステムとBATの両方の始まりのブロック、そしてその実際のサイズで構成されています。プロパティテーブルの最初の要素はルート要素です。それには2つの目的があります:ディレクトリエントリとなること(ディレクトリツリーのルート、特に)、そして小さいブロックデータの始めのブロックを保持すること。 小さいブロックデータは、4kByte以下の小さいファイルのデータを含む特別なファイルです。それは、更に小さなブロックに分かれ、特別な"small block allocation table"が(より大きいファイル用の主たるBATと同じように)小さなファイルをその小さなブロックにマッピングするのに使われます。 ヘッダ・ブロックPOIFSファイルシステムは、ヘッダブロックで始まります。ヘッダの最初64bitは、long型のファイル種別IDあるいは0xE11AB1A1E011CFD0Lという値のマジックナンバー識別子です。これは、基本的に、"sanity check"です。もし、これがヘッダの最初に無ければ(従って、ファイルシステムに)、これがPOIFSファイルシステムでは無い事を意味し、他のライブラリを使って読み込まなければならない、という事になります。 ヘッダ情報の中で最も重要な部分について知っておく必要があります。それらについては、このセクションの残りで論じていきます。 BATsオフセット0x2Cは、BAT配列内の要素のナンバーを特定するためのint値です。0x4Cにある配列は、int配列です。この配列には、Block Allocation Table内の全てのブロックのインデックスが含まれています。 XBATs非常に大きいPOIFSアーカイブは、ヘッダブロックで列挙されるBATブロックでアドレス指定されるよりも多くのブロックを必要とする事があるかもしれません。どのくらいの大きさでしょう?ええと、ヘッダ内のBAT配列は109要素のBATブロックのインデックスまで収納出来ます;各々のBATブロックは128個のブロックを参照し、各々のブロックは512バイトです。ですから、(訳注:ヘッダブロックで列挙しうるBATでは)109 * 128 * 512 = 6.8MB が限度、という事になります。まあ、(6.8MBのファイルというのは)非常に悪くないドキュメントではないですか!ですが、今日ではGBドライブも大変安くなっていますし、より大きなデータを扱うこともあるでしょう。ですから、そうなった場合は、BATを拡張することができるのです。ヘッダ内のオフセット0x44のinteger値は、最初の拡張BAT(XBAT)のインデックスです。ヘッダ内のオフセット0x48には、XBATブロックがどのくらいあるかを指定するint値があります。XBATブロックは、POIFSファイルシステムを構成しているブロック配列内の特定のインデックスで始まり、XBATブロックの指定された数のところまで連続して続きます。 各々のXBATブロックには、128個までのBATブロックのインデックスを含めることが出来ます。ですから、ドキュメントのサイズは、一つのXBATブロックあたり8MB(訳注:128*128*512バイト)まで拡張することが可能という事になります。 XBATブロックでインデックス指定されるBATブロックは、ヘッダブロックで列挙されるBATブロックのリストの後に追加されます。ですから、ヘッダブロックで列挙されるBATは、BATブロック0から108であり、一番目のXBATブロックで列挙されるBATブロックはBATブロック109から236であり、二番目のXBATブロックで列挙されるBATブロックはBATブロック237から364であり....と続いていくのです。 XBATブロックを全て使い果たすと、ドキュメントサイズの全体的な上限は、4-byte(2^32bit)のブロックインデックスにより制限されるサイズとなります;もしそのインデックスが符号無しintであれば、最大ファイルサイズは2テラバイトとなり、符号付きintとして扱われれば1テラバイトとなります(訳注:512byte = 2^9byte であり、(2^9 byte) * (2^32) = 2^41 byte = 2 * (1024)^4 byte = 2 Tbyte。符号付きの場合はその1/2)。どちらにせよ、通常のオフィスサプライ店で、それほど大きなファイルを格納できるディスクドライブを見たことはありませんけど。 SBATsPOIFSアーカイブに含まれるファイルが4096byteよりも小さい場合、小ブロック内に格納されます。小ブロックは長さ64byteを持ち、大ブロック(8個の大ブロックまで)の中に格納されます。メインのBATは大ブロックの配列をナビゲートするのに使われるのと同様、小ブロックのAllocation Table(SBAT)は小ブロックの配列をナビゲートするのに使われます。SBATの開始のインデックスはヘッダブロックのオフセット0x3Cで見つかります。また、SBATを構成するブロックの残りの部分は、POIFSファイルシステム内の通常のファイルであるかのように、メインのBATを巡回すると見つかります(このプロセスは以下で説明されます)。 Property Table Start Indexアドレス0x30にあるinteger値は、プロパティテーブルの始まりのインデックスを表します。このintegerは、"ブロックインデックス"として明記されます。プロパティテーブルは(殆ど全てのPOIFSファイルシステムがそうであるように)大きなブロック内にあり、BATを通じて巡回されます。プロパティテーブルに関しては、以下に記述されています。 プロパティテーブルプロパティテーブルは、本質的にディレクトリシステム以上の何者でもありません。プロパティは512byteブロック内に含まれる128byteレコードです。最初のプロパティは常にルートエントリです。プロパティテーブル内の個々のプロパティは以下のようになっています:
ルートエントリプロパティテーブル内のルートエントリには、小さいファイル(4096バイト長以下のファイル)を読み込んだり書き込んだりするために必要な情報が入っています。ルートエントリの開始ブロックフィールドは、小ブロック配列のインデックスの開始点であり、POIFSファイルシステムの他のファイルと同様に読み込まれます。 SBATは小ブロック配列が無い限り使えないので、ルートエントリはBlock Allocation Tableを使って読み書きされなければなりません。小ブロック配列を構成するブロックは、64バイトからルートエントリで示されるサイズ(常に64の乗数であるべきです)までの小ブロックに分けられます。 プロパティテーブルのノードの巡回個々のプロパティは、ルートエントリをディレクトリツリーのルートとしたディレクトリツリーを構成します(以下の図で表されているように)。各々のノードの括弧内の数値を押えておいてください;プロパティの配列のノードインデックスを表しています。NEXT_PROP、PREVIOUS_PROP、CHILD_PROPフィールドは、それらのインデックスを保持しており、ツリーをナビゲートするのに使われます。
各々のディレクトリエントリ(即ち、種類がディレクトリ或いはルートエントリであるプロパティ)は、CHILD_PROPを用いてその下位の(子の)プロパティの一つを指し示します。それが指し示す子がどのようなタイプであるかは関係ないようです。ですから、上の図で言えば、ルートエントリのCHILD_PROPフィールドは1と4あるいはその他子ノードの一つのインデックスを有しているということです。同じように、ディレクトリノード(インデックス番号1)は、CHILD_PROPフィールドに2、3あるいはその他子ノードの一つを有してるでしょう。 所与のディレクトリプロパティの子は、同様に、NEXT_PROPやPREVIOUS_PROPを使うことで、お互いを指し示します。 未使用のNEXT_PROP、 PREVIOUS_PROP、CHILD_PROPフィールドは、マーカーの値として-1を持っています。例えば、全てのファイルプロパティは、CHILD_PROPフィールドに-1を持っています(訳注:ファイルには、子となるノードが無いので)。 Block Allocation Tableヘッダに含まれる(及び、必要であればXBATブロックで追加される)BAT配列によって、BATブロックが指し示されます。これらのブロックは、integerの大きいテーブルを組成します。これらのintegerはブロック番号です。Block Allocation Tableはこれらintegerのチェーンを有しています。これらのチェーンは、-2の値で終了します。これらのチェーンの要素はファイル内のブロックを参照しています。ファイルの始まりのブロックはBAT内では指定されておらず、所与のファイル用のプロパティで指定されています。このBAT内の要素は、ブロック番号(ヘッダ以外のファイル内)及び次のBAT要素の数字が、チェーンを成すように入っています。ブロックのリンクリストであると思ってもらっても良いでしょう。BAT配列は一つのブロックから次々と連なっていくリンクを有しており、チェーンマーカーの「終了」も含みます。 ここで例を提示します:BATが以下のように始まると仮定しましょう: BAT[ 0 ] = 2 BAT[ 1 ] = 5 BAT[ 2 ] = 3 BAT[ 3 ] = 4 BAT[ 4 ] = 6 BAT[ 5 ] = -2 BAT[ 6 ] = 7 BAT[ 7 ] = -2 ... さて。もしプロパティテーブルのエントリによりインデックスが0で始まると指示されているファイルがあるとすると、BAT配列を渡り歩き、ファイルはブロック0(始まりのブロックは0なので),2,3,4,6,7(BAT[0]=2;BAT[2]=3;BAT[3]=4;BAT[4]=6;BAT[6]=7と、チェーンを追っていくと分かる)で構成されていることがわかります。チェーンマーカーの「終わり」を表す数字である-2がBAT[7]にありますので、ブロック7で終了します。 同様に、インデックス1で始まるファイルは、ブロック1と5で構成されます(訳注:BAT[1] = 5 ; BAT[5] = -2 // 終了)。 BAT配列におけるその他特別な数値としては:
ファイルシステム構造以下、基本的なファイルシステムの概要を示します Header (block 1) -- 512 (0x200) bytes
|