【Flutter】日記アプリのDBデータのインポート方法
DB データのファイルをインポート
個人開発で日記アプリを開発しており、データのバックアップ・インポート処理を実装する必要があるため、キャッチアップした内容を記しておきます。
前回、エクスポートに関する記事を記載していますので、先にこちらをご覧いただきたいです。今回はその記事の続編となっています。こちらの記事では DB ファイル・Txt ファイル・CSV ファイルでのエクスポート方法を紹介しており、今回はこれらのファイルをインポートする記事となります。
サンプルコード
実装例
ファイル選択
まずは、各インポート方法に共通するファイル選択処理を実装します。この部分では、file_picker
パッケージを使用してファイル選択ダイアログを表示します。
// データベースファイルをインポートする関数
Future<void> _importDatabase(BuildContext context, WidgetRef ref) async {
try {
// FilePickerを使用してファイル選択ダイアログを表示
// FileType.anyを指定することで、すべての種類のファイルを選択可能にします
final result = await FilePicker.platform.pickFiles(type: FileType.any);
// ファイルが選択された場合の処理
if (result != null && result.files.single.path != null) {
// 選択されたファイルのパスからFileオブジェクトを作成
final file = File(result.files.single.path!);
// データベースインポート処理を実行
await DiaryDatabase.instance.importDatabase(file);
// Riverpodのプロバイダーを更新して、UIを最新の状態に反映
ref.invalidate(diariesProvider);
// 成功メッセージをスナックバーで表示
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('データベースをインポートしました')),
);
// インポート完了後に前の画面に戻る
Navigator.pop(context, true);
}
} catch (e) {
// エラーが発生した場合はエラーメッセージを表示
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('インポートに失敗しました: $e')),
);
}
}
データベースファイルインポートの実装
データベースファイルを直接インポートする処理を実装します。この方法は、既存のデータベースを置き換えます。データベースを削除すると、再度 DB の初期設定等が必要となるため、今回は上書き処理としました。
/// データベースファイルを直接インポートする関数
/// @param file インポートするデータベースファイル
Future<void> importDatabase(File file) async {
// アプリケーションのデータベースディレクトリのパスを取得
// getDatabasesPath()は、プラットフォーム固有のデータベース保存場所を返します
final dbPath = await getDatabasesPath();
// 既存のデータベースファイルパスを生成
// joinは、プラットフォーム固有のパス区切り文字を使用してパスを結合します
final dbFile = File(join(dbPath, 'diary.db'));
// 新しいデータベースファイルで既存のファイルを上書き
// copyメソッドは、ファイルのバイナリコピーを作成します
await file.copy(dbFile.path);
// データベース接続を再初期化
// これにより、新しいデータベースファイルを使用するように設定されます
database = await initDB('diary.db');
}
テキストファイルインポートの実装
テキストファイルからデータをインポートする処理を実装します。特定のフォーマットに従ったテキストファイルを解析します。この方法は、一つのテーブルの読み込みであればフォーマットを解析して、苦労せずに対応可能です。ですが、複数のテーブルが存在する場合は、処理が多くなりコード量が増える可能性があるため、DB ファイルのインポートをおすすめします。正直インポートには向いていませんが、ユーザーが txt ファイルを用いて DB にインポートしたい場合は実装する必要があります。
/// テキストファイルから日記データをインポートする関数
/// テキストファイルは以下の形式である必要があります:
/// タイトル: [タイトル]
/// 本文: [内容]
Future<void> importFromTxt(File file) async {
try {
// ファイルの内容を文字列として読み込み
// readAsString()は、ファイル全体をUTF-8エンコードされた文字列として読み込みます
final content = await file.readAsString();
// ファイルの内容を行単位で分割
// split('\n')は、改行文字で文字列を分割して配列を作成します
final lines = content.split('\n');
// データベース接続を取得
final db = await instance.database;
// 各行を解析してデータベースに挿入
for (var i = 0; i < lines.length; i++) {
// タイトル行を検出
// startsWith()を使用して、行が「タイトル: 」で始まるかチェック
if (lines[i].startsWith('タイトル: ')) {
// タイトルを抽出(「タイトル: 」の部分を除去)
final title = lines[i].substring(6);
// 次の行から本文を抽出(「本文: 」の部分を除去)
final content = lines[i + 1].substring(3);
// データベースに新しい日記エントリーを挿入
// ConflictAlgorithm.replaceは、同じIDのデータが存在する場合は上書きする
await db.insert(
'diaries',
{
'title': title,
'content': content,
// 現在の日時を作成日時として使用
'created_at': DateTime.now().toIso8601String(),
},
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
}
} catch (e) {
// エラーが発生した場合は、上位層に伝播させる
rethrow;
}
}
CSV ファイルインポートの実装
CSV ファイルからデータをインポートする処理を実装します。この方法は、構造化されたデータを効率的にインポートできます。スプレットシートなどで入力して、それを DB にインポートしたい場合は活用できます。Txt ファイル同様で一つのテーブルを構造化してデータをインポートする分には問題ないが、テーブルが増えると処理が多くなるため、DB ファイルのインポートをおすすめします。
/// CSVファイルから日記データをインポートする関数
/// CSVは以下の形式である必要があります:
/// ID,タイトル,本文,作成日時
Future<void> importFromCsv(File file) async {
try {
// ファイルの内容を文字列として読み込み
final content = await file.readAsString();
// ファイルの内容を行単位で分割
final lines = content.split('\n');
// データベース接続を取得
final db = await instance.database;
// 各行を処理(1行目はヘッダーなのでスキップ)
for (var i = 1; i < lines.length; i++) {
// 行をカンマで分割してフィールドを取得
final values = lines[i].split(',');
// 必要なフィールドがすべて存在することを確認
if (values.length >= 4) {
// 各フィールドを適切な型に変換
final id = int.parse(values[0]); // IDを数値に変換
final title = values[1].replaceAll('"', ''); // ダブルクォートを除去
final content = values[2].replaceAll('"', ''); // ダブルクォートを除去
final createdAt = values[3];
// デバッグ用のログ出力
print('インポート中のデータ:');
print('ID: $id');
print('タイトル: $title');
print('本文: $content');
print('作成日時: $createdAt');
// データベースに挿入
await db.insert(
'diaries',
{
'id': id,
'title': title,
'content': content,
'created_at': createdAt,
},
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
}
} catch (e) {
// エラーが発生した場合は、上位層に伝播させる
rethrow;
}
}
以上になります。
サンプルコード
Discussion