ファイルにデータを保存する方法
shared_preferencesパッケージでは非常に小さいデータを扱うための方法でしたが、データサイズが大きい場合に利用するのがファイルを使ったデータ管理です。
事前に組み込みファイルとして用意できない画像データやメディアデータを、端末上に保存しておきたいことがよくあります。その場合には、取得したデータはファイルとして端末に保存しておきたくなります。または、アプリケーション特有の保存データを作成する場合などでも使える方法です。
ただし、PCなどと異なりスマホなどではアプリ専用の保存フォルダが限定されているので、好きな場所にファイルを保存できるわけではありません。そのため、主に以下のような流れで処理をする必要があります。
- アプリケーションで利用可能なディレクトリ名を取得する。
- 保存する(またはロードする)ファイルのインスタンスを取得する。
- ファイルにデータを保存する。またはデータをロードする。
アプリケーションで利用できるディレクトリの取得
アプリケーションで利用できるディレクトリ名を取得するには、path_providerパッケージを利用します。
path_providerパッケージを利用する場合には、以下のようにpubspec.yamlに追加します。
dependencies: : (省略) path_provider: ^2.0.11
また、このパッケージを使って取得できるすべてのOS共通のフォルダ名は表1です。ここで示すディレクトリ以外にも端末SDカードなどの追加ストレージなどのOSもしくは端末固有のディクレトリ名も取得できるので、詳しくは、こちらを参照してください。
また、スマホでよく利用するギャラリーに保存されている画像データなどにこちらのパッケージを使ってアクセスすることはできません。ギャラリーにアクセスするためには、利用者からのアクセス権限が必要になり、その方法はまた別の回で紹介します。
| フォルダ名 | 説明 |
|---|---|
| Temporary | 一時的なデータ保存領域です。起動していない時にシステムにより削除される可能性があるディレクトリです。 |
| Application Support | アプリケーションデータを保存するためのディレクトリです。 |
| Application Documents | 利用者が作成するファイルのためのディレクトリです。利用者が参照しても問題ないファイルを保存するディレクトリです。 |
実際にこれらのディレクトリを取得するためのコードがリスト4です。
import 'dart:io';
import 'package:path_provider/path_provider.dart';
Future<void> pathProvider() async{
// (1) ディレクトリの取得
Directory dir1 = await getTemporaryDirectory();
Directory dir2 = await getApplicationSupportDirectory();
Directory dir3 = await getApplicationDocumentsDirectory();
// (2) パスの取得
print("getTemporaryDirectory : ${dir1.path}");
print("getApplicationSupportDirectory : ${dir2.path}");
print("getApplicationDocumentsDirectory : ${dir3.path}");
}
// -- (3) 出力例(Android) ---
I/flutter (15408): getTemporaryDirectory : /data/user/0/com.example.chatapp/cache
I/flutter (15408): getApplicationSupportDirectory : /data/user/0/com.example.chatapp/files
I/flutter (15408): getApplicationDocumentsDirectory : /data/user/0/com.example.chatapp/app_flutter
各ディレクトリを取得するには、(1)のようにします。取得したディレクトリのパスを取得する場合には(2)のようにします。
例えば、Androidの場合には取得したディレクトリパスは(3)のようになります。
ただし、これらのパスは作成するアプリ名やパッケージ名、そしてOSによっても異なるので、これらのパスを指定して利用しないようにしてください。
ディレクトリとファイルを利用したデータの読み書き
続いて、取得したディレクトリを使ってファイルに文字列データの保存/読込する例をリスト5に示します。
import 'dart:io';
import 'package:path_provider/path_provider.dart';
Future<void> createDirAndFile() async{
Directory dir = await getApplicationSupportDirectory();
// (1) ディレクトリの作成
Directory subdir = Directory("${dir.path}/subdir");
// (2) 非同期型で記述する場合
bool has = await subdir.exists();
if(has){
await subdir.create();
}
// (3) 同期で記述する場合
if(!subdir.existsSync()){
subdir.createSync();
}
// (4) ファイルへのデータ書き込み
File file = File("${subdir.path}/sample.txt");
file.writeAsString("Hello World");
// file.writeAsStringSync("Hello World");
}
Future<void> readFile() async{
Directory dir = await getApplicationSupportDirectory();
File file = File("${dir.path}/subdir/sample.txt");
// (5) ファイルの内容の読込
String text = await file.readAsString();
// String text = file.readAsStringSync();
print("----- ${text} ----");
}
(1)ではgetApplicationSupportDirectory()で取得したディレクトリ内にサブディレクトリを作成しています。存在しないディレクトリにはファイルは作成できませんので、サブディレクトリが必要な場合にはファイルとして扱う前に作成しておく必要があります。
そして、ディレクトリの存在を確認するには、(2)もしくは(3)のようにexists()もしくはexistsSync()を利用します。これらは処理を非同期的に行うか、もしくは同期的に処理を行うかで異なります。また、ディレクトリを作成するには、create()もしくはcreateSync()を利用します。こちらも同様に同期型と非同期型の処理があります。
そして、ファイルへの文字列データの書き込みは(4)のように行い、データを読み込む場合には(5)のように行います。ファイル操作を行うメソッドにはこのように同期型と非同期型のメソッドがあります。
例えば、ファイルやディレクトリの存在確認や作成であれば、UIを止めるほどのコストは通常かかりませんので、同期処理であっても問題ないケースが多いはずです。一方で、ファイルの書込や読込では扱うデータ量の大きさで問題が違うので、一般的には非同期型のメソッドを利用する方が問題ありません。
