Objective-CでZIPアーカイブを読み取る

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で公開しました。

カテゴリー: Develop   パーマリンク

コメントをどうぞ

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

*

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">