Javaで以下のようなZip解凍用のソースコードを書いた。
ZipUtil.java
public static boolean unzip(String zipfile, String dstDir) {
// テンポラリディレクトリを作成する。
File baseDir = new File(dstDir);
if (!baseDir.mkdirs()) {
return false;
}
// Zip ファイルから ZipEntry を一つずつ取り出し、ファイルに保存していく。
ZipFile zipFile = null;
try {
zipFile = new ZipFile(zipfile);
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry ze = entries.nextElement();
// 出力先ファイル
File outFile = new File(baseDir, ze.getName());
if (ze.isDirectory()) {
// ZipEntry がディレクトリの場合はディレクトリを作成。
outFile.mkdirs();
} else {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
InputStream is = zipFile.getInputStream(ze);
bis = new BufferedInputStream(is);
if (!outFile.getParentFile().exists()) {
// ディレクトリを作成しておく
outFile.getParentFile().mkdirs();
}
bos = new BufferedOutputStream(new FileOutputStream(
outFile));
int len;
while ((len = bis.available()) > 0) {
byte[] bs = new byte[len];
bis.read(bs);
bos.write(bs);
}
} catch (FileNotFoundException e) {
throw e;
} catch (IOException e) {
throw e;
} finally {
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
}
}
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
}
}
}
}
}
return true;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return false;
}
当然Javaでの動作確認はOK.
j2objcでのコンパイルもOK.
変換後のObjcコードはこちら。
ZipUtil.m
+ (BOOL)unzipWithNSString:(NSString *)zipfile
withNSString:(NSString *)dstDir {
JavaIoFile *baseDir = [[JavaIoFile alloc] initWithNSString:dstDir];
if (![baseDir mkdirs]) {
return NO;
}
JavaUtilZipZipFile *zipFile = nil;
@try {
zipFile = [[JavaUtilZipZipFile alloc] initWithNSString:zipfile];
id<JavaUtilEnumeration> entries = [zipFile entries];
while ([((id<JavaUtilEnumeration>) nil_chk(entries)) hasMoreElements]) {
JavaUtilZipZipEntry *ze = [entries nextElement];
JavaIoFile *outFile = [[JavaIoFile alloc] initWithJavaIoFile:baseDir withNSString:[((JavaUtilZipZipEntry *) nil_chk(ze)) getName]];
if ([ze isDirectory]) {
[outFile mkdirs];
}
else {
JavaIoBufferedInputStream *bis = nil;
JavaIoBufferedOutputStream *bos = nil;
@try {
JavaIoInputStream *is = [zipFile getInputStreamWithJavaUtilZipZipEntry:ze];
bis = [[JavaIoBufferedInputStream alloc] initWithJavaIoInputStream:is];
if (![((JavaIoFile *) nil_chk([outFile getParentFile])) exists]) {
[((JavaIoFile *) nil_chk([outFile getParentFile])) mkdirs];
}
bos = [[JavaIoBufferedOutputStream alloc] initWithJavaIoOutputStream:[[JavaIoFileOutputStream alloc] initWithJavaIoFile:outFile]];
int len;
while ((len = [bis available]) > 0) {
IOSByteArray *bs = [IOSByteArray arrayWithLength:len];
[bis readWithByteArray:bs];
[bos writeWithByteArray:bs];
}
}
@catch (JavaIoFileNotFoundException *e) {
@throw e;
}
@catch (JavaIoIOException *e) {
@throw e;
}
@finally {
if (bis != nil) {
@try {
[bis close];
}
@catch (JavaIoIOException *e) {
}
}
if (bos != nil) {
@try {
[bos close];
}
@catch (JavaIoIOException *e) {
}
}
}
}
}
return YES;
}
@catch (JavaIoIOException *e) {
[((JavaIoIOException *) nil_chk(e)) printStackTrace];
}
return NO;
}
しかしいざ実行すると、それぞれ以下の行に該当するところで、IOExceptionを吐いて落ちる。
ZipUtil.java-エラー行
bis.read(bs);
ZipUtil.m-エラー行
[bis readWithByteArray:bs];
調べたが、ググっても情報は無し。
仕方ないので、Javaを次のように書き換えてみた。
ZipUtil修正版.java
public static boolean unzip(String zipfile, String dstDir) {
// テンポラリディレクトリを作成する。
File baseDir = new File(dstDir);
if (!baseDir.mkdirs()) {
return false;
}
final int BUFFER_SIZE = 1024;
try {
BufferedOutputStream dest = null;
FileInputStream fis = new FileInputStream(new File(zipfile));
ZipInputStream zis = new ZipInputStream(
new BufferedInputStream(fis));
ZipEntry entry;
File destFile;
while ((entry = zis.getNextEntry()) != null) {
destFile = new File(baseDir, entry.getName());
if (entry.isDirectory()) {
destFile.mkdirs();
continue;
} else {
int count;
byte data[] = new byte[BUFFER_SIZE];
destFile.getParentFile().mkdirs();
FileOutputStream fos = new FileOutputStream(destFile);
dest = new BufferedOutputStream(fos, BUFFER_SIZE);
while ((count = zis.read(data, 0, BUFFER_SIZE)) != -1) {
dest.write(data, 0, count);
}
dest.flush();
dest.close();
fos.close();
}
}
zis.close();
fis.close();
return true;
} catch (FileNotFoundException e) {
e.printStackTrace();
LogD(e.getMessage());
} catch (IOException e) {
e.printStackTrace();
LogD(e.getMessage());
}
return false;
}
変換後のObjective-Cのコードはこちら。
ZipUtil修正版.m
+ (BOOL)unzipWithNSString:(NSString *)zipfile
withNSString:(NSString *)dstDir {
JavaIoFile *baseDir = [[JavaIoFile alloc] initWithNSString:dstDir];
if (![baseDir mkdirs]) {
return NO;
}
int BUFFER_SIZE = 1024;
@try {
JavaIoBufferedOutputStream *dest = nil;
JavaIoFileInputStream *fis = [[JavaIoFileInputStream alloc] initWithJavaIoFile:[[JavaIoFile alloc] initWithNSString:zipfile]];
JavaUtilZipZipInputStream *zis = [[JavaUtilZipZipInputStream alloc] initWithJavaIoInputStream:[[JavaIoBufferedInputStream alloc] initWithJavaIoInputStream:fis]];
JavaUtilZipZipEntry *entry_;
JavaIoFile *destFile;
while ((entry_ = [zis getNextEntry]) != nil) {
destFile = [[JavaIoFile alloc] initWithJavaIoFile:baseDir withNSString:[((JavaUtilZipZipEntry *) nil_chk(entry_)) getName]];
if ([entry_ isDirectory]) {
[destFile mkdirs];
continue;
}
else {
int count;
IOSByteArray *data = [IOSByteArray arrayWithLength:BUFFER_SIZE];
[((JavaIoFile *) nil_chk([destFile getParentFile])) mkdirs];
JavaIoFileOutputStream *fos = [[JavaIoFileOutputStream alloc] initWithJavaIoFile:destFile];
dest = [[JavaIoBufferedOutputStream alloc] initWithJavaIoOutputStream:fos withInt:BUFFER_SIZE];
while ((count = [zis readWithByteArray:data withInt:0 withInt:BUFFER_SIZE]) != -1) {
[dest writeWithByteArray:data withInt:0 withInt:count];
}
[dest flush];
[dest close];
[fos close];
}
}
[zis close];
[fis close];
return YES;
}
@catch (JavaIoFileNotFoundException *e) {
[((JavaIoFileNotFoundException *) nil_chk(e)) printStackTrace];
[JpCoCybirdEscapeEngineLibUtilLibUtil LogDWithNSString:[e getMessage]];
}
@catch (JavaIoIOException *e) {
[((JavaIoIOException *) nil_chk(e)) printStackTrace];
[JpCoCybirdEscapeEngineLibUtilLibUtil LogDWithNSString:[e getMessage]];
}
return NO;
}
つまり、BufferedInputStream#availableでサイズを取ってそのサイズ分byte配列を確保して一気に読み込んでいたのを、固定サイズ[BUFFER_SIZE]で回しながら読む方法に変えたのだ。
そしたら、無事に実行できた。
変換後のソースの違いは、readWithByteArray
か、readWithByteArray: withInt: withInt
かの違い。
allocできる限界のサイズがあるんだろうか??
気が向いたらj2objc-discussグループにも報告しておこう・・・