LoginSignup
6
6

More than 5 years have passed since last update.

j2objc(0.9.3)において、BufferedInputStream#readでIOException

Last updated at Posted at 2014-07-29

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グループにも報告しておこう・・・

6
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6