読者です 読者をやめる 読者になる 読者になる

shobylogy

叩けシンプルの杖

zipzapで巨大なzipファイルを全展開せず、特定ファイルを取り出す

iOSアプリで巨大な巨大なzipファイルを扱おうとして困ったことありませんか?

巨大なzipファイルを扱おうとすると、主に以下のような問題が発生します。

巨大なzipファイルの問題点

  • サイズが大きすぎてメモリ上に展開できない
  • 展開に時間が掛かる
  • 展開後にストレージの容量を圧迫する

Documentディレクトリに展開するわけにもいかないし、 毎回tmpディレクトリに展開して削除するというのも考えもの。

特に画像ビューアー等では、アーカイブ内の特定のファイルだけ表示したいという場合が多く、 アーカイブを全展開するという方法はいまいちです。

そこで今回は、zipzapというライブラリを使って、zipファイル内の、特定のファイルを取り出す方法を紹介します。

zipzap

zipzapはiOS向けのzipライブラリ、 libzipのObjective-Cラッパーです。

BSD licenseなのでライセンス的にも使いやすいです。

インストール

CocoaPodsで一発インストール。簡単です。

Podfile

pod 'zipzap'

使い方

概要

zipzapは主にこんな構成になってます。

  • アーカイブ自体を扱うZZArchive
  • アーカイブ内のファイルを扱うZZArchiveEntry

ざっくりした使い方はこんな感じ。

  1. ZZArchiveを初期化
  2. ZZArchiveからZZArchiveEntryを取り出す。
  3. 取り出したZZArchiveEntryからデータを取得。

簡単ですね。

初期化

NSString *archiveFilePath = @"アーカイブファイルのパス";
ZZArchive *archive = [ZZArchive archiveWithContentsOfURL:[NSURL fileURLWithPath:archiveFilePath]];

ファイルを取り出す

とりあえず、最初のファイルを取り出してみます。

ZZArchiveEntry *entry = archive.entries[0];

NSLog(@"fileName:%@", entry.fileName);

ファイルの一覧はarchive.entriesで取得できます。 entryからファイル名も取得できるため、NSPredicateを使えば、目的のファイルのみ抽出することができます。

NSString *targetFileName = @"目的のファイル名";
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"fileName like[c] '%@'", targetFileName];
NSArray *entries = [archive.entries filteredArrayUsingPredicate:predicate];

ZZArchiveEntry *entry = [entries objectAtIndex:0];

データを取得

対象のファイルが取り出せたら、あとはそれからデータを取得するだけです。

画像ファイルだった場合はこんな感じ。

NSData *data = entry.data;

UIImage *image = [UIImage imageWithData:data scale:[UIScreen mainScreen].scale];

簡単ですね。

ちょっと待って。重いんだけど…

ZZArchiveEntryをpredicateでフィルタリングする場合、アーカイブ内の全ファイルをチェックするため、かなり重くなります。

取得したいファイルのファイル名が事前に分かっている場合、entryとファイル名の対応をNSDictionaryで保持しておくと高速にファイルを取得することができます。

entryDictionaryの生成

self.entryDictionary = [[NSMutableDictionary alloc] init];

for (ZZArchiveEntry *entry in self.archive.entries) {
  [self.entryDictionary setObject:entry forKey:entry.fileName];
}

entryDictionaryを使った取得

NSString *targetFileName = @"目的のファイル名";
ZZArchiveEntry *entry = [self.entryDictionary objectForKey:targetFileName];

まとめ

今回は、zipzapというライブラリを使って、巨大なzipファイルを展開せず、特定ファイルを取り出す方法を紹介しました。

特に画像ビューアーアプリ等では、zipファイルを展開せずに扱うメリットが大きいです。

この方法は絶賛開発中であるiTreasureMapの新バージョンで利用しており、 コード記述量も少なく、高速に動作しています。

zipzapの作者さんに敬意を払いつつ、是非ご活用下さい。

参考リンク