ZippedImages Etceteraでは、ZIPアーカイブを解凍せずに、直接読み込んでいます。
いくつかObjective-C用のZIPライブラリを試したのですが、なかなかピンと来る物が見つからず、C言語用のMinizipライブラリを使いました。
まず、Minizipの配布物から以下のファイルをプロジェクトに追加します。
crypt.h ioapi.c ioapi.h mztools.c mztools.h unzip.c unzip.h zip.c zip.h
次に、OS X付属の以下のライブラリを追加をします。iPhone OSでは試していませんが、zlibは存在するので同様の手順で大丈夫だと思います。
libz.dylib
インターフェース部分には、次のようなラッパークラスを書きました。ZippedImages Etceteraに必要な部分しか用意していませんが、ZIPアーカイブの内容を一覧して、任意のファイルを取り出せます。単純化したかったので、ストリームには対応していません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | // // ZipFile.h // ZippedImagesEtcetera // // Created by Kenji Nishishiro on 10/05/08. // Copyright 2010 Kenji Nishishiro. All rights reserved. // #import "unzip.h" @interface ZipFile : NSObject { NSString *path_; unzFile unzipFile_; } - (id)initWithFileAtPath:(NSString *)path; - (BOOL)open; - (void)close; - (NSData *)readWithFileName:(NSString *)fileName maxLength:(NSUInteger)maxLength; - (NSArray *)fileNames; @end // // ZipFile.m // ZippedImagesEtcetera // // Created by Kenji Nishishiro on 10/05/08. // Copyright 2010 Kenji Nishishiro. All rights reserved. // #import "ZipFile.h" @implementation ZipFile static const int CASE_SENSITIVITY = 0; static const unsigned int BUFFER_SIZE = 8192; - (id)initWithFileAtPath:(NSString *)path { NSAssert(path, @"path"); if (self = [super init]) { path_ = [path retain]; unzipFile_ = NULL; } return self; } - (void)dealloc { NSAssert(!unzipFile_, @"!unzipFile_"); [path_ release]; [super dealloc]; } - (BOOL)open { NSAssert(!unzipFile_, @"!unzipFile_"); unzipFile_ = unzOpen64([path_ UTF8String]); return unzipFile_ != NULL; } - (void)close { NSAssert(unzipFile_, @"unzipFile_"); unzClose(unzipFile_); unzipFile_ = NULL; } - (NSData *)readWithFileName:(NSString *)fileName maxLength:(NSUInteger)maxLength { NSAssert(unzipFile_, @"unzipFile_"); NSAssert(fileName, @"fileName"); if (unzLocateFile(unzipFile_, [fileName UTF8String], CASE_SENSITIVITY) != UNZ_OK) { return nil; } if (unzOpenCurrentFile(unzipFile_) != UNZ_OK) { return nil; } NSMutableData *data = [NSMutableData data]; NSUInteger length = 0; void *buffer = (void *)malloc(BUFFER_SIZE); while (YES) { unsigned size = length + BUFFER_SIZE <= maxLength ? BUFFER_SIZE : maxLength - length; int readLength = unzReadCurrentFile(unzipFile_, buffer, size); if (readLength 0) { [data appendBytes:buffer length:readLength]; length += readLength; } if (readLength == 0) { break; } }; free(buffer); unzCloseCurrentFile(unzipFile_); return data; } - (NSArray *)fileNames { NSAssert(unzipFile_, @"unzipFile_"); NSMutableArray *results = [NSMutableArray array]; if (unzGoToFirstFile(unzipFile_) != UNZ_OK) { return nil; } while (YES) { unz_file_info64 fileInfo; char fileName[PATH_MAX]; if (unzGetCurrentFileInfo64(unzipFile_, &fileInfo, fileName, PATH_MAX, NULL, 0, NULL, 0) != UNZ_OK) { return nil; } [results addObject:[NSString stringWithUTF8String:fileName]]; int error = unzGoToNextFile(unzipFile_); if (error == UNZ_END_OF_LIST_OF_FILE) { break; } if (error != UNZ_OK) { return nil; } } return results; } @end |
使い方は次のような感じです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | ZipFile *zipFile = [[[ZipFile alloc] initWithFileAtPath:@"ZIPアーカイブ名"] autorelease]; // ファイルを開く。 if (![zipFile open]) { // エラー処理 } // ファイルリストを取り出す。パーシャルバスで全ファイルが取得されるので、階層的に扱う必要は無い。 NSArray *fileNames = [zipFile fileNames]; if (!fileNames) { // エラー処理 } // ファイルの内容を取り出す。一度にメモリに読み込むので、ストリーム処理が必要な大きいファイルには向かない。安全のために最大サイズを指定できる。 NSData *data = [zipFile readWithFileName:@"ファイル名" maxLength:読み込む最大サイズ]; if (!data) { // エラー処理 } // ファイルを閉じる。 [zipFile close]; |
ZIPアーカイブ内の文字コードは、UTF-8を想定しています。
ZIPアーカイブ内のファイル名がShift_JISになっている場合などは、もう少し改良が必要だと思います。
2010/05/23 追記
githubで公開しました。
