Home > android > AndroidのFile入出力サンプル

AndroidのFile入出力サンプル

はじめに

Android内でファイルの保存やコピーをしたい。
そういう機能を要求するアプリは多いと思います。
私が今作ってるアプリもそうです。
さっそく実装しました。そこで失敗した事を含めて公開します。
皆様が同じ失敗を繰り返さぬよう。

実装

一般的と思われる方法でコピーを実装します。
つまり、InputStreamでファイルを読込み、書き込み用のOutputStreamに流し込みます。
#読みやすくするため例外処理を入れていません。

public static void copyFile(String srcFilePath, String dstFilePath) {
  File srcFile = new File(srcFilePath);
  File dstFile = new File(dstFilePath);
 
  // ディレクトリを作る.
  dstFile.mkdirs();
 
  // ファイルコピーのフェーズ
  InputStream input = null;
  OutputStream output = null;
  input = new FileInputStream(srcFile);
  output = new FileOutputStream(dstFile);
 
  int DEFAULT_BUFFER_SIZE = 1024 * 4;
  byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
  int n = 0;
  while (-1 != (n = input.read(buffer))) {
    output.write(buffer, 0, n);
  }
  input.close();
  output.close();
}

これは殆どApache Commons IOのFileUtils#copyFileのパクリです。

失敗

上のソースをサンプルアプリで試してみました。
サンプルは最もシンプルな形です。

package com.adamrocker.android.copy;
 
import com.adamrocker.android.util.Filer;
import android.app.Activity;
import android.os.Bundle;
 
public class FileCopyTest extends Activity {
    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.main);
        String src = "/tmp/src.txt";
        String dst = "dst.txt"; // wrong path
        Filer.copyFile(src, dst);
    }
}

dstのファイルパスが失敗の原因です。
rootディレクトリにファイルを作りにいって、失敗します。
実際コピー出来ません。
こうすれば出来ます。

/* /data/data/com.adamrocker.android.copy/dst.txt */
String dst = "/data/data/" + this.getPackageName() + "/dst.txt";

Androidはアプリケーションがそれぞれ独自のユーザIDを持ちます。

つまり、そのユーザ(アプリケーション)にwrite権限が許可されているディレクトリのみ書き込みが出来ます。その1つがアプリケーションデータ領域です。上の例はそれを指定しています。
この制限のため、他のアプリケーションのファイルを操作することが出来ずセキュアになっています。
このことに気づかず時間を取られました(汗)
ちゃんとドキュメントを読めって話ですね…。

アプリケーション領域以外にも/tmpのパーミッションが777ですので、ファイル入出力が自由です。

非公開API

この実装を終えたときに気づいたのですが、Androidには以下のAPIが準備されています。

android.os.FileUtils.copyFile(File arg0, File arg1);

公開API(m5-rc14)のドキュメントでは表示されていないAPIです。
ドキュメントで公開されていないAPIはいったいどれぐらいあるんでしょうね?
探してみると面白い物が見つかるかもしれませんね。

ということで、このAPIを利用してファイルコピーを実装し直しました。

public static void copyFile(String srcFilePath, String dstFilePath) {
  File srcFile = new File(srcFilePath);
  File dstFile = new File(dstFilePath);
 
  // make directory for the destination file.
  //dstFile.mkdirs(); <-WRONG
  dstFile.getParentFile().mkdirs();
 
  // copy file
  FileUtils.copyFile(srcFile, dstFile);
}

とっても簡単になりました。
(* mkdirsが間違ってましたので修正しました。2008.3.24)

ついでに

アプリケーションデータ領域のみでファイルの入出力ストリームを作るなら簡単なAPIが準備されています。

public abstract class Context {
  public abstract FileInputStream openFileInput(String name);
  public abstract FileOutputStream openFileOutput(String name, int mode);
}

ContextはActivityクラスによって継承されています。
なので、簡単に入出力ストリームを得る事が出来ます。

package com.adamrocker.android.copy;
 
import com.adamrocker.android.util.Filer;
import android.app.Activity;
import android.os.Bundle;
 
public class FileCopyTest extends Activity {
    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.main);
        String src = "src.txt";
        String dst = "dst.txt";
        InputStream input = openFileInput(src);
        OutputStream output = openFileOutput(dst, Context.MODE_WORLD_READABLE);
    }
}

ちなみに、このメソッドで入出力されるファイルは、

/data/data/[package name]/files/

このディレクトリ内だけです。
もし、srcやdstにセパレータ文字(”/”)が含まれていると、IllegalArgumentExceptionでcontains a path separatorと言われて実行出来ません。

おわりに
いさみあし

ドキュメント探してFile IO系のAPIが無いと思って実装したのに、非公開のAPIが存在して凹んだ…orz

ジャカルタのが語音が好き

Apache Commonsはいつの間にJakarta Commonsから改名したんだろう?

関連のありそうなエントリ

Comments:0

Comment Form
Remember personal info

*
To prove that you're not a bot, enter this code
Anti-Spam Image

Trackbacks:0

Trackback URL for this entry
http://www.adamrocker.com/blog/196/android_file_io.html/trackback/
Listed below are links to weblogs that reference
AndroidのFile入出力サンプル from throw Life

Home > android > AndroidのFile入出力サンプル

Search
Feeds
Meta

Return to page top