AndroidでZIPファイルの圧縮・展開をする


Androidではファイルの圧縮技術としてZIPファイルフォーマットを利用することができます。
生の状態で保存しておくにはサイズが大きいファイルを扱う場合は
ZIP形式で圧縮しておくといったことなど、様々な用途に利用することができます。

AndroidでZIP形式のファイルを扱うには、主に下記3つのクラスがポイントになります。

  • ZipOutputStreamクラス
  • ZipInputStreamクラス
  • ZipEntryクラス

ZipOutputStreamクラスはZIP形式でファイルを書き込むための出力ストリームフィルタです。
Androidで通常のファイルを書き出したい場合にはFileOutputStreamクラスなどのファイル出力ストリームを使いますが、
このときにZipOutputStreamクラスを組み合わせて使うとZIP形式でファイルを書き込むことができるようになります。
FileInputStreamクラスで元のデータを読み込み、ZipOutputStreamクラスでZIP変換したデータを出力します。

展開する場合も同様にFileInputStreamクラスでZIPファイルを読み込み、ZipInputStreamクラスで元のデータに変換するような流れになります。

更に重要なポイントとしてZipEntryクラスがあります。
ZIPファイルはそのZIPファイルに含まれるファイルやディレクトリの情報をエントリと呼ばれる形で保持しています。
このエントリを扱うためのクラスがZipEntryクラスです。

具体的なサンプルコードは続きからどうぞ。

ZIP形式でファイルを圧縮する

それではZIP形式でファイルを圧縮するサンプルコードをご紹介します。
下記は、SDカードなどに保存されているJPEGファイルをZIP圧縮するサンプルコードになります。

	public void compress(List inputFiles, String outputFile) {
		// 入力ストリーム
		InputStream is = null;

		// ZIP形式の出力ストリーム
		ZipOutputStream zos = null;

		// 入出力用のバッファを作成
		byte[] buf = new byte[1024];

		// ZipOutputStreamオブジェクトの作成
		try {
			zos = new ZipOutputStream(new FileOutputStream(outputFile));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

		try {
			for (int i = 0; i < inputFiles.size(); i++) {
				// 入力ストリームのオブジェクトを作成
				is = new FileInputStream((String)inputFiles.get(i));

				// Setting Filename
				String filename = String.format("img_%02d.jpg", i);
				
				// ZIPエントリを作成
				ZipEntry ze = new ZipEntry(filename);

				// 作成したZIPエントリを登録
				zos.putNextEntry(ze);

				// 入力ストリームからZIP形式の出力ストリームへ書き出す
				int len = 0;
				while ((len = is.read(buf)) != -1) {
					zos.write(buf, 0, len);
				}

				// 入力ストリームを閉じる
				is.close();

				// エントリをクローズする
				zos.closeEntry();
			}

			// 出力ストリームを閉じる
			zos.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

compressメソッドの引数は、それぞれ以下のようになっています。

  • List inputFiles : 圧縮したいJPEGファイルのリスト
  • String outputFile : 出力先となるZIPファイルのファイル名

メソッドの内容についてですが、まず13行目でZipOutputStreamオブジェクトを作成しています。
引数には出力先となるZIPファイルを指定したFileOutputStreamオブジェクトを設定します。

次に19行目から始まるループで、リストに登録されているJPEG画像に対して順番に処理を行います。
ループ内の21行目でリストからJPEG画像のファイルパスを取得し、FileInputStreamオブジェクトを生成します。
更に27行目でZipEntryを作成し、ファイル名を登録します。
なお、ZipEntryに登録するファイル名は重複できませんので注意が必要です。
※ここでは1つのJPEGファイルを異なるファイルとしてZipEntryに登録しているため、
 24行目のようにそれぞれオリジナルとは異なるファイル名を作成し、それをZipEntryに登録しています。

そして、34行目から36行目にかけて、入力ストリームからZIP形式の出力ストリームへ書き出しています。

書き出しが完了したら、39行目で入力ストリームを閉じ、42行目でZipEntryも閉じます。
ZipEntryを閉じた時点で、1つのファイルの圧縮が完了することになります。
そして最後に46行目でZipOutputStreamをクローズすることでZIPファイル全体の作成が完了します。

ZIP形式の圧縮ファイルを展開する

次にZIP形式の圧縮ファイルを展開するサンプルを紹介します。

	public void extract(String filename) {
		ZipInputStream in = null;
		BufferedOutputStream out = null;

		ZipEntry zipEntry = null;
		int len = 0;
		
		try {
			in = new ZipInputStream(new FileInputStream(filename));

			// ZIPファイルに含まれるエントリに対して順にアクセス
			while ((zipEntry = in.getNextEntry()) != null) {
				File newfile = new File(zipEntry.getName());

				// 出力用ファイルストリームの生成
				out = new BufferedOutputStream(
						new FileOutputStream(FILE_DIR_PATH + newfile.getName())
					  );

				// エントリの内容を出力
				byte[] buffer = new byte[1024];
				while ((len = in.read(buffer)) != -1) {
					out.write(buffer, 0, len);
				}

				in.closeEntry();
				out.close();
				out = null;
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

extractメソッドの引数は以下のようになっています。

  • String filename : 展開したいZIPファイルのファイル名

extractメソッドの内容ですが、まず9行目でZIPファイルを入力元とするFileInputStreamオブジェクトを作成します。
更にこのオブジェクトを入力元にしたZipInputStreamオブジェクトを作成します。

12行目でgetNextEntryメソッドでZIPファイル内に含まれるZipEntryを順番に読み出して行きます。
13行目でZipEntryからファイル名を取得し、16行目~18行目でこのファイル名の出力ファイルストリームを生成します。

そして、22行目~24行目で入力ストリームからデータを読み出しながら、出力ファイルにデータを書き出します。

26行目でZipEntryをクローズし、27行目で出力ストリームを閉じます。

以上でZIP形式の圧縮ファイルを展開することができます。

※java.util.zip.ZipOutputStreamは、日本語ファイル名に対応していません。
もし日本語ファイル名に対応する場合にはorg.apache.tools.zip.ZipOutputStreamなどで対応することができます。

インターネットの画像をZIP圧縮する

最後に、上記で説明したcompressメソッドとextractメソッドを実際に使うサンプルとして、
インターネット上の画像ファイルをダウンロードし、ZIP圧縮するコードを紹介します。

テキストボックスに画像ファイルのURLを入力し圧縮ボタンを押すと、対象の画像ファイルを
ダウンロードし、ZIP形式で圧縮します。
更に展開ボタンを押すと、圧縮したZIPファイルを展開します。

public class ZipArchiverActivity extends Activity {
	// ログの表示用TextView
	TextView tv = null;	
	// 1つのZIPファイルに含める画像ファイル数
	final int FILE_COPY_NUM = 3;
	// ストリームのバッファサイズ
	final int BUFFER_SIZE = 1024;
	// ダウンロードする画像ファイルや作成したZIPファイルを保存しておくディレクトリ
	final String FILE_DIR_PATH = Environment.getExternalStorageDirectory() + "/archive/";
	// 作成するZIPファイル
	final String ZIP_FILENAME = FILE_DIR_PATH + "compress.zip";
	
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		// パフォーマンス低下を検出する機能を無効にしておく
		StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
				.permitAll().build());

		Button button = (Button) findViewById(R.id.button1);
		Button extract_button = (Button) findViewById(R.id.button2);
		
		tv = (TextView) findViewById(R.id.textView1);

		// ダウンロードした画像などの保存ディレクトリを作成する
		File filedir = new File(FILE_DIR_PATH);
		filedir.mkdirs();

		// ダウンロード&圧縮開始ボタンが押されたときの処理
		button.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				// EditTextから画像URLを取得し、ダウンロードする
				EditText url = (EditText) findViewById(R.id.editText1);
				String downloadedFilePath = download(url.getText().toString()); // 戻り値は画像の保存先のパス
				
				if (downloadedFilePath != null) {
					List inputFiles = new ArrayList();
					
					// ZIP圧縮したいファイルのパスをリストに追加(ここでは同一ファイルを3つ)
					for (int i=0; i<FILE_COPY_NUM; i++) {
						inputFiles.add(downloadedFilePath);
					}
					
					// 圧縮開始
					compress(inputFiles, ZIP_FILENAME);
				}
			}
		});

		// 展開開始ボタンが押されたときの処理
		extract_button.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				// 展開開始
				extract(ZIP_FILENAME);
			}
		});
	}

	/**
	 * ダウンロード・インストールメソッド
	 */
	public String download(String apkurl) {
		try {
			// URL設定
			URL url = new URL(apkurl);

			// HTTP接続開始
			HttpURLConnection c = (HttpURLConnection) url.openConnection();
			c.setRequestMethod("GET");
			c.connect();

			// テンポラリファイルの設定
	        File outputFile = File.createTempFile("imagefile", ".jpg");
			FileOutputStream fos = new FileOutputStream(outputFile);

			// ダウンロード開始
			InputStream is = c.getInputStream();
			byte[] buffer = new byte[BUFFER_SIZE];
			int len = 0;
			
			while ((len = is.read(buffer)) != -1) {
				fos.write(buffer, 0, len);
			}
			
			fos.close();
			is.close();

			// ダウンロードした画像ファイルのパスを返す
			return outputFile.getPath();
		} catch (IOException e) {
			e.printStackTrace();
			
			// エラーの場合nullを返す
			return null;
		}
	}

以上、お疲れさまでした。

関連する記事:

No Comments