Flutterに限りませんが、Google Driveを使用してアプリケーション領域にバックアップ、リストアをしたい場合、色々と設定がややこしくて詰まるので、自分用の備忘録として確認すべき項目をリスト化しておきます。
他の人にも役立てば幸いです。
自分のPC
- keytoolが使用できる事(自分の場合、パスが通っていないので[Java > jdk-xxx > bin](keytool.exeがあるフォルダ)まで移動)
- コマンドでデバッグキーストアの取得
keytool -list -v -alias androiddebugkey -keystore C:\Users\YOUR_USERNAME\.android\debug.keystore
* [YOUR_USERNAME] 自分のユーザー名
* [キーストアのパスワード] 通常は「android」
- 表示された「SHA-1」(key1)と「SHA-256」(key2)をコピペしておく
- コマンドでリリースキーストアの作成と取得
keytool -genkey -v -keystore my-release-key.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000
* [my-release-key.keystore] 生成されるキーストアの名前
* [my-key-alias] キーエイリアスの名前
- キーストアのパスワードとキーエイリアスのパスワードを設定後、名前、組織部署、組織名(会社名)、都市名(市)、州(県)、国コード(JP)を入力して、[はい]でキーストアの作成
- コマンドで作成したキーストアのフィンガープリントを取得
keytool -list -v -alias my-key-alias -keystore my-release-key.keystore
* [my-key-alias] 先程の作成したキーエイリアスの名前
* [my-release-key.keystore] 先程の作成したキーストアの名前
- キーストアのパスワードとキーエイリアスのパスワードを入力して、表示された「SHA-1」(key3)と「SHA-256」(key4)をコピペしておく
Google Play
- アプリの登録(大前提)(テスト公開でOK)
- [設定 > アプリの署名]から「SHA-1 証明書のフィンガープリント」(key5)「SHA-256 証明書のフィンガープリント」(key6)を取得
Firebase
- プロジェクトの作成(大前提)
- アプリの追加(Android)
- [Authentication > Sign-in method(ログイン方法)]のログインプロバイダで「Google」を有効にする
- [プロジェクトの概要 > プロジェクトの設定]もしくは[プロジェクト > アプリ]を選択
- [マイアプリ]のアプリを選択して「フィンガープリントを追加」する(key1, key2, key3, key4, key5, key6)
- google-services.jsonをダウンロードしておく
Google Cloud
- プロジェクトの作成(大前提)
- [ナビゲーションメニュー > APIとサービス > ライブラリ]に移動
- 「Google Drive」を検索して、「Google Drive API」を有効にする
- [APIとサービス > OAuth 同意画面]に移動
- [公開ステータス]は「テスト」(公開したら本番環境へ)
- [ユーザーの種類]は「外部」
- [スコープの設定]は「Google Drive」で検索して「…/auth/drive.file」と「…/auth/drive.appdata」を追加
Flutter
- (ここからルートはアプリのトップディレクトリ)
- [android/app]にgoogle-services.jsonを追加
- [android/build.gradle]に以下を追加
dependencies {
...
// versionはその都度合わせる
classpath 'com.google.gms:google-services:4.3.15'
}
- [android/app/build.gradle]に以下を追加
plugins {
...
id 'com.google.gms.google-services'
}
dependencies {
...
implementation(platform("com.google.firebase:firebase-bom:32.7.0"))
implementation("com.google.firebase:firebase-analytics")
implementation("com.google.firebase:firebase-auth")
implementation("com.google.firebase:firebase-firestore")
}
- 「flutter pub add」コマンドで必要なパッケージを取得
- google_sign_in
- firebase_core
- firebase_auth
- flutter_secure_storage
- googleapis
- googleapis_auth
- http
Flutterでの接続について
設定はGoogle Play, Firebase, Google Cloudがちゃんとしていれば大丈夫のはずです。下はバックアップ、リストア、削除のクラス(google_drive_api.dart)です。
*参考サイト「【Flutter】GoogleDriveへのバックアップ・リストア機能を実装するまでの道のり」
import 'dart:async';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:googleapis/drive/v3.dart' as ga;
import 'package:http/io_client.dart';
import 'package:http/http.dart' as http;
import 'package:path/path.dart' as path;
import 'dart:io' as io;
class GoogleApi {
Future<bool> uploadFileToGoogleDrive() async {
bool isUpload = false;
final completer = Completer<bool>();
try {
GoogleSignIn googleSignIn = GoogleSignIn(
scopes: [
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive.appdata',
],
);
GoogleSignInAccount? googleSignInAccount = await googleSignIn.signIn();
if (googleSignInAccount != null) {
var client = GoogleHttpClient(await googleSignInAccount.authHeaders);
var drive = ga.DriveApi(client);
ga.FileList list = await drive.files.list(spaces: 'appDataFolder', $fields: 'files(id, name, modifiedTime)');
// Google Driveに残っているアプリフォルダ内のデータを削除しておく
if (list.files != null && list.files!.isNotEmpty) {
for (var i = 0; i < list.files!.length; i++) {
String? gdId = list.files![i].id;
if (gdId != null) {
await drive.files.delete(gdId);
}
}
}
ga.File fileToUpload = ga.File();
String dbPath = "ファイルのフルパス";
if (io.File(dbPath).existsSync()) {
io.File dbFile = io.File(dbPath);
fileToUpload.parents = ["appDataFolder"];
fileToUpload.modifiedTime = DateTime.now();
fileToUpload.name = path.basename(dbFile.path);
await drive.files.create(
fileToUpload,
uploadMedia: ga.Media(dbFile.openRead(), await dbFile.length()),
);
isUpload = true;
completer.complete(isUpload);
} else {
// バックアップするファイルが見つかりません
completer.complete(isUpload);
}
} else {
// Googleのサインインに失敗しました
completer.complete(isUpload);
}
} catch (e) {
// エラー
completer.completeError(e);
}
return completer.future;
}
Future<bool> downloadGoogleDriveFile() async {
bool isDownload = false;
final completer = Completer<bool>();
try {
GoogleSignIn googleSignIn = GoogleSignIn(
scopes: [
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive.appdata',
],
);
GoogleSignInAccount? googleSignInAccount = await googleSignIn.signIn();
if (googleSignInAccount != null) {
var client = GoogleHttpClient(await googleSignInAccount.authHeaders);
var drive = ga.DriveApi(client);
// Google Driveのアプリフォルダに保存されている一つ目のデータを取得
ga.FileList list = await drive.files.list(spaces: 'appDataFolder', $fields: 'files(id, name, modifiedTime)');
String? gdID = (list.files != null && list.files!.isNotEmpty) ? list.files!.first.id : "";
if (gdID != null && gdID.isNotEmpty) {
ga.Media file = await drive.files.get(gdID, downloadOptions: ga.DownloadOptions.fullMedia) as ga.Media;
String dbPath = "ファイルのフルパス";
io.File dbFile = io.File(dbPath);
List<int> dataStore = [];
file.stream.listen((data) {
dataStore.insertAll(dataStore.length, data);
}, onDone: () {
dbFile.writeAsBytes(dataStore);
isDownload = true;
completer.complete(isDownload);
}, onError: (error) {
// エラー
completer.completeError(error);
});
} else {
// ダウンロードするデータが見つかりませんでした
completer.complete(isDownload);
}
} else {
// Googleのサインインに失敗しました
completer.complete(isDownload);
}
} catch (e) {
// エラー
completer.completeError(e);
}
return completer.future;
}
// バックアップデータの削除
Future<bool> deletedGoogleDriveFile() async {
bool isDelete = false;
final completer = Completer<bool>();
try {
GoogleSignIn googleSignIn = GoogleSignIn(
scopes: [
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive.appdata',
],
);
GoogleSignInAccount? googleSignInAccount = await googleSignIn.signIn();
if (googleSignInAccount != null) {
var client = GoogleHttpClient(await googleSignInAccount.authHeaders);
var drive = ga.DriveApi(client);
ga.FileList list = await drive.files.list(spaces: 'appDataFolder', $fields: 'files(id, name, modifiedTime)');
// Google Driveに残っているアプリフォルダ内のデータを削除しておく
if (list.files != null && list.files!.isNotEmpty) {
for (var i = 0; i < list.files!.length; i++) {
String? gdId = list.files![i].id;
if (gdId != null) {
await drive.files.delete(gdId);
}
}
}
isDelete = true;
completer.complete(isDelete);
} else {
// Googleのサインインに失敗しました
completer.complete(isDelete);
}
} catch (e) {
// エラー
completer.completeError(e);
}
return completer.future;
}
}
class GoogleHttpClient extends IOClient {
final Map<String, String> _headers;
GoogleHttpClient(this._headers) : super();
@override
Future<IOStreamedResponse> send(http.BaseRequest request) => super.send(request..headers.addAll(_headers));
@override
Future<http.Response> head(Uri url, {Map<String, String>? headers}) {
headers = headers ?? {};
headers.addAll(_headers);
return super.head(url, headers: headers);
}
}
使う場合は下みたいな感じで。
import 'package:notebook/google_drive_api.dart';
// こんな感じで使うか
bool isUpload= await GoogleApi().uploadFileToGoogleDrive();
message = (isUpload) ? "バックアップ成功" : "バックアップ失敗";
// こんな感じで使う
GoogleApi().uploadFileToGoogleDrive().then((bool isUpload) {
var message = (isUpload) ? "バックアップ成功" : "バックアップ失敗";
});
これで上手くいきました。
ちなみにここまでBingと二人三脚でやりました。Bing、めちゃくちゃ優秀です。
ここまでやって接続できない、エラーが出る人は私にとっても勉強になるので教えてください。
コメントを残す