導入
roadmap.shのFlutterの`Advanced Dartについて学習を進めていきます。
今回はdart:io
ライブラリです。
このライブラリは、ファイル、ディレクトリ、プロセス、ソケット、WebSocket、HTTPクライアント、サーバーを扱うためのAPIを提供してくれます。
dart:io
dart:io
ライブラリは、ウェブアプリではないのFlutterアプリ、コマンドラインスクリプト、サーバーでのみインポートして使用できます。
ウェブアプリでは使用できません。
dart:io
ライブラリは一般的に非同期APIを実装することが推奨されています。同期処理の場合、その処理がアプリケーションをブロックしてしまい、スケールさせることが難しくなる可能性があるためです。
そのため、ほとんどの操作はFuture
やStream
オブジェクトを介して結果を返します。
同期メソッドもdart:io
ライブラリに一応存在はしており、メソッド名に「Sync」サフィックスが付いています。
dart:io
ライブラリもdart:math
などと同様に、使用する際には、以下のようにインポートが必要になります。
import 'dart:io';
ファイルとディレクトリの操作
今回のライブラリを使用すると、コマンドラインアプリでファイルの読み書きやディレクトリのブラウズが可能です。
ファイルの内容を読み込む方法は2通りあります。
- すべてを一度に読み込む
- ファイルの内容をすべて格納できるだけのメモリが必要
- ストリームを使用して読み込む
- ファイルが非常に大きい場合や、読み込みながら処理したい場合は、
Stream
を使用するのが良い
- ファイルが非常に大きい場合や、読み込みながら処理したい場合は、
テキストとしてファイルを読み込む
UTF-8でエンコードされたテキストファイルを読み込む場合、readAsString()
を使用してファイル全体を文字列として読む込むことができます。逆に行ごとに取得したい場合は、readAsLines()
を使用することで実現可能です。
いずれの場合も、Future
オブジェクトが返却されます。
void main() async {
var config = File('config.txt');
// ファイル全体を1つの文字列に格納
var stringContents = await config.readAsString();
print('ファイルは${stringContents.length}文字の長さです。');
// ファイルの各行をそれぞれの文字列に格納
var lines = await config.readAsLines();
print('ファイルには${lines.length}行あります。');
}
バイナリとしてファイルを読み込む
ファイル全体をバイトのリストとして読み込むことも可能です。
readAsBytes()
の呼び出しはFuture
を返却します。
結果が利用可能になった時点でその結果を提供してくれます。
void main() async {
var config = File('config.txt');
var contents = await config.readAsBytes();
print('ファイルは${contents.length}バイトの長さです。');
}
エラーハンドリング
エラーが発生した際、未処理の例外にならないようにするには、Future
にcatchError
ハンドラを登録するか、非同期関数内でtry-catch
を使用します。
void main() async {
var config = File('config.txt');
try {
var contents = await config.readAsString();
print(contents);
} catch (e) {
print(e);
}
}
ストリームでファイル内容を読み込む
Stream
を使用してファイルを少しずつ読み込むことができます。
Stream
APIや、Dartの非同期サポートの一部であるawait for
を使用できます。
import 'dart:io';
import 'dart:convert';
void main() async {
var config = File('config.txt');
Stream<List<int>> inputStream = config.openRead();
var lines = utf8.decoder.bind(inputStream).transform(const LineSplitter());
try {
await for (final line in lines) {
print('ストリームから${line.length}文字を取得');
}
print('ファイルが閉じられました');
} catch (e) {
print(e);
}
}
ファイル内容の書き込み
データをファイルに書き込むには、IOSink
を使用します。
File
のopenWrite()
メソッドを使用して、書き込み可能なIOSink
を取得します。
FileMode.write
は、デフォルトのモードであり、ファイル内の既存のデータを完全に上書きします。
完全上書きでなく、ファイルの末尾にデータを追加したい場合は、オプションのmode
パラメータでFileMode.append
を指定します。
var logFile = File('log.txt');
// ファイルに完全上書きしたい場合
var sink = logFile.openWrite();
sink.write('ファイルがアクセスされました ${DateTime.now()}\n');
await sink.flush();
await sink.close();
// 末尾に追加したい場合
var sink = logFile.openWrite(mode: FileMode.append);
バイナリデータを書き込むには、add(List<int> data)
を使用します。
ディレクトリ内のファイル一覧を取得
次は特定のファイルに対するI/Oではなく、ディレクトリに対する操作についてです。
ディレクトリ内のすべてのファイルとサブディレクトリを検索する操作は非同期になります。list()メソッドは、ファイルやディレクトリが見つかるたびにオブジェクトを出力してくれる
Stream`を返却します。
void main() async {
var dir = Directory('tmp');
try {
var dirList = dir.list();
await for (final FileSystemEntity f in dirList) {
if (f is File) {
print('ファイルを発見: ${f.path}');
} else if (f is Directory) {
print('ディレクトリを発見: ${f.path}');
}
}
} catch (e) {
print(e.toString());
}
}
HTTPクライアントとサーバー
dart:io
ライブラリは、コマンドラインアプリがHTTPリソースにアクセスしたり、HTTPサーバーを実行するためのクラスを提供します。
これを利用することで、HTTPサーバーアプリケーションをFlutterで作成することが可能になります。
HTTPサーバー
HttpServer
クラスは、ウェブサーバーを構築するための低レベルの機能を提供します。リクエストハンドラのマッチング、ヘッダーの設定、データのストリーミングなどが可能です。
次のサンプルウェブサーバーは、シンプルなテキスト情報を返します。このサーバーはポート8888で127.0.0.1
(localhost)を監視し、/dart
パスへのリクエストに応答します。他のパスへのリクエストには、ステータスコード404(ページが見つかりません)で応答します。
void main() async {
final requests = await HttpServer.bind('localhost', 8888);
await for (final request in requests) {
processRequest(request);
}
}
void processRequest(HttpRequest request) {
print('リクエストを受信: ${request.uri.path}');
final response = request.response;
if (request.uri.path == '/dart') {
response
..headers.contentType = ContentType(
'text',
'plain',
)
..write('サーバーからの挨拶');
} else {
response.statusCode = HttpStatus.notFound;
}
response.close();
}
HTTPクライアント
dart:io
を使用して直接HTTPリクエストを行うのは避けた方が良いとされています。dart:io
のHttpClient
クラスはプラットフォーム依存であり、単一の実装に結びついています。代わりに、package:http
のような高レベルのライブラリを使用することが推奨されています。
終わりに
今回はdart:io
ライブラリについて学習しました。File操作によく利用されるライブラリですが、HTTP通信にも利用できることは言葉上でしか理解できていなかったので、為になりました。ただ、モバイルアプリとして開発する場合は、Flutterはクライアントとなるので、終盤に書いた通り、package:http
のようなライブラリを使用した方が良さそうです。
その点も含めて、今回学習できたことはプラスになったと思うので、今後もこういったCoreライブラリを活用していきたいです。