いわゆるアドベンチャー・ゲームみたいに、複数のデータファイルを扱う場合、それらをひとつのファイルにまとめることがほとんどだよね。
その理由は、機密性が第一なのかな?
でも配布を考えた場合、ひとつにまとまっていた方が都合いいんだ。
まず、コピーが早い。
ファイルの取りこぼしがない。
それと一概にはいえないけど、通信パケットを節約できる。
で、ひとつにまとめるには、どうしたらいいか?
色々と方法はあるけど、オイラはzipを使うことにしている。
なぜかといえば、まとめるツールは作らなくていいし、取り出すルーチンも簡単ですむから。
というワケで、zipからデータを取り出すmethod を作ってみたよ。
ZipFile と ZipEntry
zip を扱うのに、使うclass は以下の2つ。
●ZipFile
http://developer.android.com/reference/java/util/zip/ZipFile.html
●ZipEntry
http://developer.android.com/reference/java/util/zip/ZipEntry.html
ZipFile は、そのままZIP を抽象化したもの。
ZipEntry は、ZIPの中のファイルを抽象化したもの。
method を眺めると、ZipEntry と Fileクラス はなんか似ている。
たぶん、「ZipFile はHD、ZipEntry はファイル」と見立ててるんだろうね。
取り出すときもInputStream を使うから、ファイル操作と手順はそう変わらなそう。
ちなみに、ZipInputStream というclass もある。
今回は使わなかったけど、役割はZipFile と同じみたいだ。
おそらく、ネットからZIPを取得した時、メモリー上にZIPがある時、なんかに使うんだろね。
ファイル一覧を取得する
まず、中のファイル一覧を取得してみた。
下記method は、ZIPのパスを指定すると、中のファイル一覧をString配列として返すよ。
public int err = 0; // エラーがあったか? // -1 : エラー有り | other : エラーなし public String[] getList( String zipPath ){ err = 0; String[] result = null; //-- try{ ZipFile zipFile = new ZipFile( zipPath ); List<String> list = new ArrayList<String>(); Enumeration<? extends ZipEntry> enu = zipFile.entries(); while( enu.hasMoreElements() ){ ZipEntry entry = enu.nextElement(); //-- list.add( entry.getName() ); } zipFile.close(); result = (String[])list.toArray(new String[0]); }catch( Exception ex ){ err = -1; Log.d( "Err", "// ZipIO : getList err... //" ); } //-- return result; }
まず、ZIPのパスから、ZipFile オブジェクトを生成する。
そして、entries() で、Enumeration を取得する。
このEnumeration は、中のファイル、──つまりZipEntry の塊みたいなモンだと思っていい。
Enumeration のnextElement() を使うと、順繰りにZipEntry を取り出せるんだ。
で、取り出したZipEntry にgetName() をして、ファイル名を取得している。
取得したファイル名は、いったんList オブジェクトに格納するよ。
List オブジェクトは配列と似たようなものだけど、要素数を増やすことができるんだ。
お陰でファイルの総数がわからなくても、配列を作ることができる。
そして、ファイル名の格納が終わったら、List をtoArray()でString配列に変換するんだ。
以上で、ファイル一覧を取得できた。
応用として、getName() をgetSize() にすると、容量一覧が取得できるね。
※.getSize() は、long を返します。
注意点は、ZipFile のclose。
最後にclose してやらないと、メモリーリークする可能性があるから注意。
文字バケ
一覧を取得できたので、画面に表示してみた。
…と。なんかバケてるね。(^_^;
Windowsで作ったZIPなので、ファイル名がShift-JISなのかもしれない。
それじゃ、Macで作ったZIPならバケない…?
…見事にバケてるよ。(w
これはたぶん、Javaレベルの問題。
1バイト文字専用で、2バイトやUTF-8に対応していないんだね。
バケ方のちがいからすると、そんな感じ。
2バイト文字に対応するには、取得したファイル名をデコードしてやらないといけないんだね。
まぁ、解凍には支障ないんで、今回はパスしておくよ。
ファイルを解凍してみた
次に、ファイルを指定して、解凍してみた。
下記method で、指定したファイルを解凍するよ。
手順は、ファイルのコピーとそう変わらないね。
引数は、
ZIPのパスを指定
解凍するファイル名
解凍先(Path付ファイル名)
public int err = 0; // エラーがあったか? // -1 : エラー有り | other : エラーなし public void meltFile( String zipPath, String file, String outFile ){ err = 0; try{ ZipFile zipFile = new ZipFile( zipPath ); ZipEntry entry = zipFile.getEntry( file ); InputStream is = zipFile.getInputStream( entry ); OutputStream os = new FileOutputStream( outFile ); byte[] buffer = new byte[ 1024*4 ]; // 1024*4 = DEFAULT BUFFER SIZE int r = 0; while( -1 != ( r = is.read(buffer)) ){ os.write( buffer, 0, r ); } os.close(); is.close(); zipFile.close(); }catch( Exception ex ){ err = -1; Log.d( "Err", "// ZipIO : meltFile err... //" ); } //-- }
ディレクトリの中にファイルがある場合、解凍するファイル名をPath付にしてやる。
こんな感じ。
dir/char.gif
ただし、2バイトの場合、↓ではダメ。
新しいフォルダ/char.gif
※.UTF-8 のZIPなら解凍できるかも…?
たぶん、「1バイト化け」した状態で指定してやれば解凍できるんじゃないかな?
もしくはファイル名以外の方法で、ZipEntry を取得してやればいいと思う。
ちなみに上記サンプルでは、解凍先にディレクトリは作られない。
必要があるなら、別途つけくわえてね。
画像データを取得してみた
データとして取り出すにはどうしたらいいんだろ?
ここにひとつ問題があるんだ。
一覧取得でちょっと触れたけど、ファイルサイズの単位は、long なんだ。
これではbyte配列自体が作れない。
int サイズに限定すればできるけど、そういう制限はかけたくない。
となると、InputStream から直接、目的のデータにすることになりそう。
というワケで。
画像データ(Bitmap)として取り出すmethod を作ってみた。
public int err = 0; // エラーがあったか? // -1 : エラー有り | other : エラーなし public Bitmap meltBitmap( String zipPath, String file ){ err = 0; Bitmap result = null; //-- try{ ZipFile zipFile = new ZipFile( zipPath ); ZipEntry entry = zipFile.getEntry( file ); InputStream is = zipFile.getInputStream( entry ); result = BitmapFactory.decodeStream( is ); is.close(); zipFile.close(); }catch( Exception ex ){ err = -1; Log.d( "Err", "// ZipIO : meltBitmap err... //" ); } //-- return result; }
BitmapFactory で、InputStream からBitmap にしている。
この方法は、ネットから画像データを取得するのと同じ方法。
Drawable で取り出す時には、
Drawable.createFromStream
を使えばいいよ。
取得した画像データをImageView で表示してみた。
問題なし。(^_^)
※.画像サイズが大きいと、失敗することがあるらしい。
詳しいくは…
「ZipFile ~画像サイズが大きいと読み込み失敗」
by the way…
個人的に困ったのは、RandomAccessFile が使えないことだね。
ヘッダー位置の取得方法がないから、部分的に読み込むことができない。
できないワケじゃないけど、イロイロと面倒なんだな、これが。(^_^;
一度テンポラリーに、ファイルとして解凍するのがいいのかな…。