- 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:6
- ゲスト 09-07-01 (水) 18:53
-
android.os.FileUtils.copyFile(File arg0, File arg1);
これは非公開なのに使ってもいいですか?
またソース書いたらエラーになっちゃいますね。
- adamrocker 09-07-01 (水) 19:28
-
>ゲストさん
cupcakeではなくなったみたいですね。 - ゲスト 09-07-02 (木) 12:24
-
ありがとうございます。
ファイルマネジャーの一部機能実装していますけど
/sdcardの画像をコピーした場合
—-rw-rw- system system 310862 2009-04-17 23:30 1239978643996.jpg
のものがFileInputStream、FileOutputStream、でコピーされて
たとえば/data/wwwフォルダに移動するとパーミッションが変わって下記のようになりますね。(-rw——-)
-rw——- app_54 app_54 307803 2009-07-01 19:22 1239978904726.jpgこれをイメージビューアで開くと画像が表示されない。
chown 066してみてもパーミッションが変わらない。なにかいい方法がありますか?
- ゲスト 09-07-03 (金) 10:37
-
Context.openFileOutput() ですが /data/data//filesしか出力できないようですね。
たとえば/data/data//files/www に出力しようとするとエラーになります。
自分のパッケージ内部(/data/data//)で自由にファイル作成、ディレクトリ作成ができればいいのに。。
なにかいい方法がありますでしょうか?
もしかして自由なファイル作成、ディレクトリ作成などは SDcard しかできないでしょうか?
- adamrocker 09-07-03 (金) 10:44
-
>ゲストさん
パッケージ内はfilesしか無理みたいです。
セキュリティの問題がありますから、この制限は緩和されないと予想します。
なので、どうしても自由にしたいならSDカードを使うのが良いのかも… - ゲスト 09-07-03 (金) 12:03
-
>SDcard しかできないでしょうか
ごめんなさい、ほかのエラーでした。
試したところ、/filesのなかは自由に出来ますね。
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
