Android Tips #46 Zip ファイルを展開して SD カードに保存する

catch

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

Android で Zip を展開するには

このたび案件で Zip を取り扱う機会があったので Zip ファイルを展開する方法についてまとめておきたいと思います。Zip ファイルを展開するには大きく3つのクラスが絡んできます。下記のような感じです。

ZipInputStream

このクラスは Zip ファイルを展開するための InputStream クラスです。引数には対象の Zip ファイルの InputStream を渡します。getNextEntry メソッドで Zip ファイルに含まれるファイルのデータをひとつずつ取得することができます。

ZipEntry

このクラスは Zip ファイル内のひとつのファイル、またはディレクトリのアイテムのオブジェクトです。getName メソッドでファイル名を取得できたり getSize メソッドでファイルサイズを取得できたりします。

ZipFile

このクラスは Zip の中にあるデータの InputStream を取得するためのクラスです。まずコンストラクタ引数に Zip ファイルのパスを渡すことでその Zip ファイルを扱う ZipFile インスタンスが生成できます。getZipEntity メソッドでその Zip に含まれている ZipEntity が取得できます。 getInputStream メソッドの引数に ZipEntry インスタンスを渡すと、対象のファイルの InputStream を取得することができます。

Zip を展開してみる

ということで Zip ファイルを展開してみましょう。今回は下図のような5つの画像ファイルが含まれている Zip ファイルを展開し、SDカード直下の sample フォルダに保存していくサンプルを実装したいと思います!

zip01

まず Zip ファイル自体を一時ファイルとして保存します。今回はわざわざ用意するのはちょっと面倒なので、プロジェクトの assets フォルダ内に Zip ファイルを置いておき、そこから FileInputStream を取得して一時ファイルとして保存します。

// Zipファイルを一時ファイルとして保存
File file = null;
try {
    file = File.createTempFile("sample", "zip");
    // Webからダウンロードするなど、何らかの形でZipファイルを取得
    InputStream is = getResources().getAssets().open("sample.zip");
    FileOutputStream fos = new FileOutputStream(file);
    byte[] buffer = new byte[1024];
    int length = 0;
    while ((length = is.read(buffer))>0) {
        fos.write(buffer, 0, length);
    }
    fos.close();
    is.close();
} catch (IOException e) {
    e.printStackTrace();
}

次に Zip ファイルの中身をひとつずつ取り出して SD カードに保存していきます。まずは展開したファイルを取り扱いやすいよう、適当にクラスを作ります。このクラスはファイルのフルパスとファイル名を保持しておくためのクラスです。

OpendZipEntry.java

package jp.classmethod.android.sample.zipinput;

public class OpendZipEntry {
    
    public String fileName;
    public String filePath;

}

次が一番重要な処理です。まずは ZipInputStream を使って Zip ファイルを展開し getNextEntryZipEntry を取得します。そのあと ZipFilegetInputStream で各ファイルの InputStream を取得していき FileOutputStream で書き出していきます。

// Zipファイルを展開して保存
ArrayList<OpendZipEntry> list = new ArrayList<OpendZipEntry>();
try {
    // Zipファイルを開く
    ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(file));
    ZipFile zipFile = new ZipFile(file);
    // 保存するディレクトリを作成
    File dir = new File(Environment.getExternalStorageDirectory() + "/sample");
    if (!dir.exists()) {
        dir.mkdir();
    }
    // 順番に読み込む
    ZipEntry entry = null;
    while((entry = zipInputStream.getNextEntry()) != null) {
        // Listで表示するアイテム
        OpendZipEntry item = new OpendZipEntry();
        item.fileName = entry.getName();
        item.filePath = dir.getPath() + "/" + item.fileName;
        list.add(item);
        // データのコピー
        InputStream is = zipFile.getInputStream(entry);
        FileOutputStream os = new FileOutputStream(new File(item.filePath));
        byte[] buffer = new byte[1024];
        int length = 0;
        while ((length = is.read(buffer))>0) {
            os.write(buffer, 0, length);
        }
        os.close();
        is.close();
        zipInputStream.closeEntry();
    }
    zipInputStream.close();
} catch (IOException e) {
    Log.e(TAG, "IOException");
}

この処理までで Zip ファイルの解凍は終わりです!あとは OpendZipEntry クラスでファイル名・ファイルパスは保持してあるので、ListView などで引っ張ってきたりは自由自在です。ゲームのアセットデータであれば SQLite に格納したりですかね。

zip02

ソースコード

今回のソースコードを GitHub に公開しました!
一番最後に書いてある ListView で拾ってくるところも実装しているので、ぜひ参考にしてください。メモリ管理とかは省略しているのでそこは別途実装が必要ですので、自分で拡張してください。

suwa-yuki/ZipInputSample

まとめ

今回は Zip ファイルの展開について取り扱ってみました。今回の実装はそこまで難しいことはないのですが、階層構造や、画像ファイル以外のデータや、大きいファイルなどを取り扱おうとするともう少し複雑になると思われます。。まぁ今回は導入編ということで!w

参考