Flutter Web: Indexed DBをファイルストレージとして使う
モバイルからWebに移行する際の簡単なテクニック
この記事は「Flutter Web: Use Indexed DB as File Storage」を翻訳したものです。
Flutterは、複数のプラットフォームにアプリを公開するための最も優れたソリューションの一つです。Flutter for mobileをFlutter for Webに移行する場合、通常はいくつかの変更を加えるだけで済みます。
しかし、Flutter for mobileでローカルファイルストレージを使用している場合、Web上でローカルファイルをどのように扱うかという、少し悩ましい問題があります。
私はその解決策の一つとして、Indexed DBを見つけました。ファイルストレージとしてどのように使っているかを説明します。
dart:ioはFlutter Webでサポートされていない
ローカルファイルを扱いたい場合は、Dart言語がデフォルトで提供しているdart:ioパッケージを使うのが一般的です。使い方は以下のようになります。
import 'dart:io'; final file = File(filePath);
if (file.existsSync()) {
data = file.readAsBytesSync();
}
しかし、残念ながらFlutter Webでは、dart:ioパッケージを使うことができません。そのため、別の方法を探さなければなりません。
IndexedDBを使う
dart:ioの代替策として見つけたのがこれです。
IndexedDBは、ユーザーのブラウザ内にデータを永続的に保存するための方法です。 — MDN
IndexedDBは、大規模なNoSQLストレージシステムです。ユーザーのブラウザにあらゆるものを保存することができます。 — Google Developers
dart:ioのFileクラスと同じ機能を実現するために、Indexed DBを後ろに隠したクラスを考えました。こんな感じです。
final idbFile = IdbFile(filePath);
if (await idbFile.exists()) {
data = await idbFile.readAsBytes();
}
上記のコードでは、dart:ioのFileクラスをオリジナルのIdbFileクラスに変更しただけです。また、同期的に動作する機能を実装するのは難しいと思われるので、awaitキーワードを追加しています。
どうですか?このようなクラスがあれば、通常のFileクラスと同じように使えると思いませんか?
idb_shim — インデックス付きDBを使用するためのDartパッケージ
idb_shimというDartパッケージを見つけました。Indexed DBを簡単に使えるようになるので便利です。公式サイトから使い方の例を紹介します。
// define the store name
const String storeName = "records";
// open the database
Database db = await idbFactory.open("my_records.db", version: 1,
onUpgradeNeeded: (VersionChangeEvent event) {
Database db = event.database;
// create the store
db.createObjectStore(storeName, autoIncrement: true);
});
// put some data
var txn = db.transaction(storeName, "readwrite");
var store = txn.objectStore(storeName);
var key = await store.put({"some": "data"});
await txn.completed;
// read some data
txn = db.transaction(storeName, "readonly");
store = txn.objectStore(storeName);
Map value = await store.getObject(key);
await txn.completed;
これと似たようなパッケージがいくつかあるので注意が必要です。ただし、それらのパッケージの中には、永続的なストレージではなく、メモリ上でのみ動作するものもあります。
データベース構造
ファイルストレージとして使用するために、Indexed DBのオブジェクトストア構造(名前は「files」)を以下のように考えました。(オブジェクト・ストアはテーブルのようなもので、プロパティはカラムのようなものです)

非常にシンプルな構造になっていますね。実際に使うには他にもいろいろな属性があったほうがいいかもしれませんが、とりあえず使うにはこれで十分です。
このオブジェクトストアにデータを保存すると、Chromeデベロッパーツールで、このように実際のデータを確認することができます。
ソースコード
サンプルのソースコードはこちらから入手できます。
結論
私はすでに、この方法を使って、私のプライベートなFlutterアプリをモバイルからWebに移行しました。そして、このアプリがうまく動くことに満足しています。
約500KBのPDFファイルを使用していますが、ネイティブとWebのパフォーマンスに違いは感じられませんでした。
IdbFileクラスには、改善の余地がたくさんあります。例えば、ディレクトリ管理、ファイル検索などです。
このクラスを自分なりに改良して、この記事を参考にしていただければ、とても嬉しいです。