- 2008-03-22 12:22
- android
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ですので、ファイル入出力が自由です。
この実装を終えたときに気づいたのですが、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); } }
ちなみに、このメソッドで入出力されるファイルは、
このディレクトリ内だけです。
もし、srcやdstにセパレータ文字(”/”)が含まれていると、IllegalArgumentExceptionでcontains a path separatorと言われて実行出来ません。
ドキュメント探してFile IO系のAPIが無いと思って実装したのに、非公開のAPIが存在して凹んだ…orz
Apache Commonsはいつの間にJakarta Commonsから改名したんだろう?
- Newer: gungi vol.08に参加してきました
- Older: AndroidでSQLiteを利用した実用的な入力補完の実装
Comments:0
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
