| Apache | Jakarta | POI |
POIFS API の使用方法POIFS API の使用方法この文書は、どのようにPOIFS APIを使って、それらの内容を組織する為にPOIFSに互換性のあるデータ構造を採用するファイルの読み込み・書き出し・修正を行うか、について、記述します。 改訂履歴
対象となる方々この文書は、どのようにPOIFS APIを使って、それらの内容を組織する為にPOIFSに互換性のあるデータ構造を採用するファイルの読み込み・書き出し・修正を行うか、について必要性のあるJava開発者を対象にしています。開発者がPOIFSのデータ構造を理解しておく必要はありません、また、それらのデータ構造の説明については、この文書の扱う範囲を超えています。基礎的な階層的ファイルシステムへの理解、そしてAWT等といったJava APIにより採用されるイベントパターンについての精通がある事は、この文書の対象となる方々にとって有益であることと期待しております。 用語集この文書は、技術用語に関して一貫性を保つ努力がなされています。その技術用語については、こちらで定義されます:
ファイルシステムの読み込みこのセクションでは、ファイルシステムの読み込みに関することを取り扱います。ファイルシステムを読み込むためには2つの方法があります;それらのテクニックについては、以下のテーブルで概要が示されており、その更に下のセクションでより深い詳細について説明がなされています。
通常の読み込みこのテクニックを使う場合、ファイルシステム全体がメモリに格納され、アプリケーションは全てのディレクトリツリーを走査する事が出来ます。アプリケーションが都合の良いときに指定のドキュメントを読み込む事が出来ます。 下準備アプリケーションがファイルシステムからファイルを読み込む前に、ファイルシステムはメモリ内にロードされてある必要があります。これは、org.apache.poi.poifs.filesystem.POIFSFileSystemクラスを使うことで成されます。一旦ファイルシステムがメモリ上にロードされれば、アプリケーションがルートディレクトリを必要とすることもあるでしょう。以下のコード(一部)は、このような下準備段階を満たすものとなります:
// need an open InputStream; for a file-based system, this would be appropriate:
// InputStream stream = new FileInputStream(fileName);
POIFSFileSystem fs;
try
{
fs = new POIFSFileSystem(inputStream);
}
catch (IOException e)
{
// an I/O error occurred, or the InputStream did not provide a compatible
// POIFS data structure
}
DirectoryEntry root = fs.getRoot();
例外が投げられなかったことを仮定すれば、ファイルシステムが読み込まれたことになります。 注意:ファイルシステムのロードには結構時間がかかります。特に、大きいファイルシステムである場合には。 ディレクトリツリーの読み込み一旦ファイルシステムがメモリ上にロードされてルートディレクトリが取得されると、ルートディレクトリを読み取ることが可能となります。以下のコード(一部)は、どのようにorg.apache.poi.poifs.filesystem.DirectoryEntryインスタンス内のエントリを読み取るかの例を提示します:
// dir is an instance of DirectoryEntry ...
for (Iterator iter = dir.getEntries(); iter.hasNext(); )
{
Entry entry = (Entry)iter.next();
System.out.println("found entry: " + entry.getName());
if (entry instanceof DirectoryEntry)
{
// .. recurse into this directory
}
else if (entry instanceof DocumentEntry)
{
// entry is a document, which you can read
}
else
{
// currently, either an Entry is a DirectoryEntry or a DocumentEntry,
// but in the future, there may be other entry subinterfaces. The
// internal data structure certainly allows for a lot more entry types.
}
}
指定ドキュメントの読み込みドキュメントの読み込みには2つの方法があり、それは、ドキュメントがルートディレクトリにあるかそれとも別のディレクトリにあるかに依存しています。どちらの方法にせよ、org.apache.poi.poifs.filesystem.DocumentInputStreamインスタンスを得ることになります。 DocumentInputStreamDocumentInputStreamクラスは、多少特筆する価値のある保証を与えるInputStreamのシンプルな実装であります:
以下のように、availableメソッドを使うことで、ドキュメント内で1回だけのread()メソッド呼び出しを行うだけで済むようになります: byte[] content = new byte[ stream.available() ]; stream.read(content); stream.close(); mark, reset, skipを組み合わせて使うことで、ドキュメントの内容へのランダムアクセスの為の基本的なメカニズムが提供されます。 ルートディレクトリからのドキュメントの読み込みもし、ルートディレクトリにドキュメントがあれば、DocumentInputStreamを以下のようにして得ることが出来ます:
// load file system
try
{
DocumentInputStream stream = filesystem.createDocumentInputStream(documentName);
// process data from stream
}
catch (IOException e)
{
// no such document, or the Entry represented by documentName is not a
// DocumentEntry
}
任意ディレクトリにあるドキュメントの読み込みドキュメントの読み込みのより一般的なテクニックは、ターゲットとするドキュメントを含むディレクトリのorg.apache.poi.poifs.filesystem.DirectoryEntryインスタンスを取得する事です(ファイルシステムからルートディレクトリを得るにはgetRoot()が使えることを思い出してください)。このDirectoryEntryから、以下のようにDocumentInputStreamを取得することが出来ます: DocumentEntry document = (DocumentEntry)directory.getEntry(documentName); DocumentInputStream stream = new DocumentInputStream(document); イベント稼動型の読み込みドキュメント読み込み用のイベント稼動型APIは、多少より複雑であり、予めアプリケーションがどのファイルを読み込もうとしているのかを覚えさせておく必要があります。このAPIを使うメリットは、アプリケーションがドキュメントを読み込む際必要とするに十分なだけのメモリしか使われない、ということであり、決して読み込もうとしないドキュメントは、メモリの中には完全に存在しない、という事です。ターゲットとするドキュメントを読み込み終えれば、ファイルシステムはそれに関連したデータ構造をまったく保持せず、破棄される事が可能となります。 下準備org.apache.poi.poifs.eventfilesystem.POIFSReaderのインスタンスの作成を伴い、またPOIFSReaderに1つかそれ以上のorg.apache.poi.poifs.eventfilesystem.POIFSReaderListenerインスタンスを登録しておく事もこの下準備フェーズに含まれます。
POIFSReader reader = new POIFSReader();
// register for everything
reader.registerListener(myOmnivorousListener);
// register for selective files
reader.registerListener(myPickyListener, "foo");
reader.registerListener(myPickyListener, "bar");
// register for selective files
reader.registerListener(myOtherPickyListener, new POIFSDocumentPath(),
"fubar");
reader.registerListener(myOtherPickyListener, new POIFSDocumentPath(
new String[] { "usr", "bin" ), "fubar");
POIFSReaderListenerorg.apache.poi.poifs.eventfilesystem.POIFSReaderListenerは、ドキュメント登録の際に使用されるインタフェースです。マッチするドキュメントがorg.apache.poi.poifs.eventfilesystem.POIFSReaderにより読み込まれた際、POIFSReaderListenerインスタンスはorg.apache.poi.poifs.eventfilesystem.POIFSReaderEventインスタンス(すなわちオープンされた状態のDocumentInputStreamとドキュメントに関する情報を含むインスタンス)を受け取ります。 POIFSReaderListenerインスタンスは、各々のあるいは全てのドキュメントを登録することが出来ます;一旦全てのドキュメントの登録がなされれば、その後に続く(及び、其の前にすでにやってしまった!)個々のドキュメントの登録要求は無視されます。POIFSReaderListenerの登録を破棄する手段はありません。 従って、1つのPOIFSReaderListenerを複数のドキュメント(1つ、多少、あるいは全てのドキュメント)を登録することが出来ます。登録されるドキュメントに付き丁度一つの通知が1つのPOIFSReaderListenerに受け渡される事が保証されています。ドキュメントの通知を受け取る順番につき、保証はありません。POIFSReaderの将来的な実装で、ファイルシステムのディレクトリ構造動き回るアルゴリズムを変更することを自由に行えるようにする為です。 同一のドキュメントに対し1つ以上のPOIFSReaderListenerを登録することも出来ます。 POIFSReaderが同一ドキュメントを処理する際、そのドキュメント用に登録されたPOIFSReaderListenerインスタンスの通知の順番に保証はありません。 全ての通知が同一のスレッドで発生することが保証されています。将来的な拡張で、マルチスレッドの通知が行われるようにするかもしれませんが、そのような拡張を行う場合、新しいreaderクラス(ThreadedPOIFSReaderとかかな?)を作らなければならない可能性が非常に高いでしょう。 以下のテーブルでは、ドキュメントやドキュメントのセット用にPOIFSReaderListenerを登録するための3つの方法を紹介します:
POIFSDocumentPathorg.apache.poi.poifs.filesystem.POIFSDocumentPathクラスは、POIFSファイルシステム内のディレクトリを表現するのに使われます。POIFSファイルシステムの中には、ファイル名の予約されたキャラクタが無いので、(ディレクトリ名の構成要素を定めている特殊キャラクタ:"/"とかを使った)ディレクトリ名を記述する文字列ベースの古典的なソリューションはうまくいかないでしょう。このクラスのコンストラクタは、以下のように使用します:
POIFSReaderEventイベントの処理org.apache.poi.poifs.eventfilesystem.POIFSReaderEventイベントの処理は、比較的やさしいです。全てのPOIFSReaderListenerがPOIFSReaderに登録された後、POIFSReader.read(InputStream stream)が呼ばれます。 データに問題が無いと仮定すれば、 POIFSReaderが指定されたInputStreamのデータのドキュメント処理を行うと、 POIFSReaderEventに登録されたPOIFSReaderListenerインスタンスのprocessPOIFSReaderEventメソッドがコールされます。 POIFSReaderEventインスタンスには、ドキュメントを特定する情報が含まれています(ドキュメントが入っているディレクトリを特定するPOIFSDocumentPathオブジェクトとドキュメント名)。また、ドキュメントが最初に読み込まれる、オープンされたDocumentInputStreamインスタンスも含まれます。 ファイルシステムの書き込みファイルシステムへの書き込みは、やり方が複数あるという点でも、ファイルシステムからの読み込みと非常によく似ています。既存のファイルシステムをメモリ上にロードし、それを修正(ファイルの移動、ファイル名の変更)及び/或いはそこに新たなファイルの追加や書き込みも可能ですし、又は、空の新しいファイルシステムからはじめる事も出来ます: POIFSFileSystem fs = new POIFSFileSystem(); 命名についてファイルを作成する際、考慮しなければならないファイルシステム上のファイル名の制約が2つほどあります:
ドキュメントの作成DirectoryEntryを取得し、2つのうちの1つのcreateDocumentメソッドをコールすることで、ドキュメントの作成を行うことが出来ます:
読み込みの時と異なり、メモリ型・イベント稼動型の書き込みモデルを選択する、などという必要はありません;同一のファイルシステムで共存することが可能となっています。 POIFSFileSystemインスタンスのwriteFilesystem()メソッドが書き込み用のOutputStreamと共に呼ばれた際、書き込み処理が初期化されます。 イベント稼動型モデルは、読み込みの場合のイベント稼動型モデルと非常によく似ています。ドキュメントを読み込むタイミングの際、POIFSReaderがPOIFSReaderListenerを呼び出すのと同様に、ドキュメントを書き出すタイミングの際、ファイルシステムがorg.apache.poi.poifs.filesystem.POIFSWriterListenerを呼び出すのですから。内部的には、writeFilesystem()メソッドが呼ばれた際、最終的なPOIFSデータ構造が作成され、特別なOutputStreamにその構造が書き込まれます。イベント稼動型モデルで作成されたドキュメントをファイルシステムが書き込む必要がある際、POIFSWriterListenerがコールバックされ、processPOIFSWriterEvent()メソッドが(org.apache.poi.poifs.filesystem.POIFSWriterEventインスタンスが渡されて)呼び出されます。このオブジェクトは、POIFSDocumentPathや、ドキュメントの名前、サイズ、オープンされたorg.apache.poi.poifs.filesystem.DocumentOutputStream(どこに書き込むべきか)といった情報を含んでいます。DocumentOutputStreamは、書き込むべきPOIFSFileSystemに対して与えられたOutputStreamのラッパーとなります。そして、アプリケーションが書き込むドキュメントが、指定したサイズ内におさまるかどうかを保証する役割を担います。 ディレクトリの作成ディレクトリの作成は、ドキュメントの作成とよく似ていますが、唯一、方法が一つしかない点が異なります: DirectoryEntry createdDir = existingDir.createDirectory(name); ドキュメント・ディレクトリ作成にPOIFSFileSystemを直接使用するドキュメントの読み込みに関して言えば、POIFSFileSystemの便利なメソッドを使ってルートディレクトリ内に新規ドキュメントや新規ディレクトリを作成することが可能です。
ファイルシステムの修正アプリケーションがメモリに既にロードされているものであれ、当に今しがた作成中のものであれ、既存のPOIFSファイルシステムの修正をする事が可能です。 ドキュメントの削除ドキュメントの削除は単純です:ドキュメントに対応するEntryを取得し、そのdelete()メソッドを呼び出すだけです。これは、ブール値を返すメソッドですが、(処理が成功したことを示す)true値が常に戻るようです。 ディレクトリの削除ディレクトリの削除も単純です:ディレクトリに対応するEntryを取得し、そのdelete()メソッドを呼び出すだけです。これは、ブール値を返すメソッドですが、ドキュメントの削除とは異なり、(処理が成功したことを示す)true値が常に戻るというわけではありません。以下に、(処理が失敗する場合の)その理由を記述します:
ファイル・ディレクトリ名の変更ファイルであろうがディレクトリであろうが、名前の変更は可能です - が、唯一の例外があります。ルートディレクトリは、メジャーなソフトウェアベンダのOfficeスイートによって予想される特別な名前を持っています。ですから、POIFS APIは、其の名前を変更することが出来ません。ファイルに対応したEntryインスタンスを取得し、新規の名前を入れた状態でrenameToメソッドを呼び出すことで、名前の変更を行うことが出来ます。 処理が上手く行けばdeleteと同じように、renameToはtrueを返しますし、そうでなければfalseを返します。失敗する原因には、以下のようなものもあります:
|
| Special Thanks -- 【お問い合わせ/テキスト広告】 |