Flutter:如何像 WhatsApp 一样在 Google 驱动器上备份用户数据?
Flutter: How to backup user data on Google drive like WhatsApp does?
我想在用户 Google Drive 帐户的 Backups 中备份数据并恢复它们。
我见过 WhatsApp 等应用程序允许用户通过 google 驱动器登录并定期备份到用户云。
我不想使用 firebase cloud,因为数据是由用户自己访问的,而不是其他用户访问的,如果数据很大,成本会很高。有没有可用的包可以做到这一点?或者教程,否则如何在 flutter 中实现这个?
步骤 1
您需要已经创建了一个 Google Firebase 项目,并从 Google 开发者控制台启用 Google Drive API。请注意,您需要在 Google 开发者控制台中 select 与您在 Google Firebase 中创建的相同项目。
步骤 2
您需要使用 google 登录才能获取 googleSignInAccount 并使用
依赖项
googleapis: ^0.54.0
googleapis_auth: ^0.2.11
class GoogleHttpClient extends IOClient {
Map<String, String> _headers;
GoogleHttpClient(this._headers) : super();
@override
Future<http.StreamedResponse> send(http.BaseRequest request) =>
super.send(request..headers.addAll(_headers));
@override
Future<http.Response> head(Object url, {Map<String, String> headers}) =>
super.head(url, headers: headers..addAll(_headers));
}
将文件上传到 Google 驱动器
_uploadFileToGoogleDrive() async {
var client = GoogleHttpClient(await googleSignInAccount.authHeaders);
var drive = ga.DriveApi(client);
ga.File fileToUpload = ga.File();
var file = await FilePicker.getFile();
fileToUpload.parents = ["appDataFolder"];
fileToUpload.name = path.basename(file.absolute.path);
var response = await drive.files.create(
fileToUpload,
uploadMedia: ga.Media(file.openRead(), file.lengthSync()),
);
print(response);
}
下载Google 驱动器文件
Future<void> _downloadGoogleDriveFile(String fName, String gdID) async {
var client = GoogleHttpClient(await googleSignInAccount.authHeaders);
var drive = ga.DriveApi(client);
ga.Media file = await drive.files
.get(gdID, downloadOptions: ga.DownloadOptions.FullMedia);
print(file.stream);
final directory = await getExternalStorageDirectory();
print(directory.path);
final saveFile = File('${directory.path}/${new DateTime.now().millisecondsSinceEpoch}$fName');
List<int> dataStore = [];
file.stream.listen((data) {
print("DataReceived: ${data.length}");
dataStore.insertAll(dataStore.length, data);
}, onDone: () {
print("Task Done");
saveFile.writeAsBytes(dataStore);
print("File saved at ${saveFile.path}");
}, onError: (error) {
print("Some Error");
});
}
这很简单,但您必须通过一系列步骤才能完成它。
1.启用 Google 驱动器 API
点击 link 到 google 控制台:
https://console.developers.google.com/apis/library?project=diary-app-339509
- 创建一个新项目并在该项目上启用 Drive API。
- 为 android 和 IOS
创建一个 OAuth 客户端 ID
- 创建它需要填写很多详细信息,通读页面并填写
- 复制客户端 ID 以备将来使用
create project and enable API
Create OAuth client id
form to fill to make OAuth client id
2。使项目可供外部使用。
for testing add test user email address
Flutter 上的编码部分
3。将以下包添加到 pubspec.yaml
googleapis: ^7.0.0
googleapis_auth: ^1.3.0
flutter_secure_storage: ^5.0.2
url_launcher: ^6.0.0-nullsafety
4。从 google 获取身份验证并存储身份验证详细信息以供将来进行身份验证
使用以下 class 存储授权详细信息
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:googleapis_auth/auth_io.dart';
class SecureStorage {
final storage = FlutterSecureStorage();
//Save Credentials
Future saveCredentials(AccessToken token, String refreshToken) async {
print(token.expiry.toIso8601String());
await storage.write(key: "type", value: token.type);
await storage.write(key: "data", value: token.data);
await storage.write(key: "expiry", value: token.expiry.toString());
await storage.write(key: "refreshToken", value: refreshToken);
}
//Get Saved Credentials
Future<Map<String, dynamic>?> getCredentials() async {
var result = await storage.readAll();
if (result.isEmpty) return null;
return result;
}
//Clear Saved Credentials
Future clear() {
return storage.deleteAll();
}
}
5.Finally 使用此 class 将文件保存到云端硬盘
import 'dart:io';
import 'package:googleapis/drive/v3.dart' as ga;
import 'package:googleapis_auth/auth_io.dart';
import 'package:http/http.dart' as http;
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'package:personal_diary/app/utils/secure_storage.dart';
import 'package:url_launcher/url_launcher.dart';
const _clientId = "379503391554-6d33j9e6lan7lg0ac7sj81026u8nigs8.apps.googleusercontent.com";
const _scopes = ['https://www.googleapis.com/auth/drive.file'];
class GoogleDrive {
final storage = SecureStorage();
//Get Authenticated Http Client
Future<http.Client> getHttpClient() async {
//Get Credentials
var credentials = await storage.getCredentials();
if (credentials == null) {
//Needs user authentication
var authClient = await clientViaUserConsent(
ClientId(_clientId),_scopes, (url) {
//Open Url in Browser
launch(url);
});
//Save Credentials
await storage.saveCredentials(authClient.credentials.accessToken,
authClient.credentials.refreshToken!);
return authClient;
} else {
print(credentials["expiry"]);
//Already authenticated
return authenticatedClient(
http.Client(),
AccessCredentials(
AccessToken(credentials["type"], credentials["data"],
DateTime.tryParse(credentials["expiry"])!),
credentials["refreshToken"],
_scopes));
}
}
// check if the directory forlder is already available in drive , if available return its id
// if not available create a folder in drive and return id
// if not able to create id then it means user authetication has failed
Future<String?> _getFolderId(ga.DriveApi driveApi) async {
final mimeType = "application/vnd.google-apps.folder";
String folderName = "personalDiaryBackup";
try {
final found = await driveApi.files.list(
q: "mimeType = '$mimeType' and name = '$folderName'",
$fields: "files(id, name)",
);
final files = found.files;
if (files == null) {
print("Sign-in first Error");
return null;
}
// The folder already exists
if (files.isNotEmpty) {
return files.first.id;
}
// Create a folder
ga.File folder = ga.File();
folder.name = folderName;
folder.mimeType = mimeType;
final folderCreation = await driveApi.files.create(folder);
print("Folder ID: ${folderCreation.id}");
return folderCreation.id;
} catch (e) {
print(e);
return null;
}
}
uploadFileToGoogleDrive(File file) async {
var client = await getHttpClient();
var drive = ga.DriveApi(client);
String? folderId = await _getFolderId(drive);
if(folderId == null){
print("Sign-in first Error");
}else {
ga.File fileToUpload = ga.File();
fileToUpload.parents = [folderId];
fileToUpload.name = p.basename(file.absolute.path);
var response = await drive.files.create(
fileToUpload,
uploadMedia: ga.Media(file.openRead(), file.lengthSync()),
);
print(response);
}
}
Future<void> _downloadGoogleDriveFile(String fName, String gdID) async {
var client = await getHttpClient();
var drive = ga.DriveApi(client);
ga.Media file = await drive.files.get(gdID, downloadOptions: ga.DownloadOptions.FullMedia);
print(file.stream);
final directory = await getExternalStorageDirectory();
print(directory);
final saveFile = File('$directory/${DateTime.now().millisecondsSinceEpoch}$fName');
List<int> dataStore = [];
file.stream.listen((data) {
print("DataReceived: ${data.length}");
dataStore.insertAll(dataStore.length, data);
}, onDone: () {
print("Task Done");
saveFile.writeAsBytes(dataStore);
print("File saved at ${saveFile.path}");
}, onError: (error) {
print("Some Error");
});
}
}
我想在用户 Google Drive 帐户的 Backups 中备份数据并恢复它们。
我见过 WhatsApp 等应用程序允许用户通过 google 驱动器登录并定期备份到用户云。
我不想使用 firebase cloud,因为数据是由用户自己访问的,而不是其他用户访问的,如果数据很大,成本会很高。有没有可用的包可以做到这一点?或者教程,否则如何在 flutter 中实现这个?
步骤 1
您需要已经创建了一个 Google Firebase 项目,并从 Google 开发者控制台启用 Google Drive API。请注意,您需要在 Google 开发者控制台中 select 与您在 Google Firebase 中创建的相同项目。
步骤 2
您需要使用 google 登录才能获取 googleSignInAccount 并使用 依赖项
googleapis: ^0.54.0
googleapis_auth: ^0.2.11
class GoogleHttpClient extends IOClient {
Map<String, String> _headers;
GoogleHttpClient(this._headers) : super();
@override
Future<http.StreamedResponse> send(http.BaseRequest request) =>
super.send(request..headers.addAll(_headers));
@override
Future<http.Response> head(Object url, {Map<String, String> headers}) =>
super.head(url, headers: headers..addAll(_headers));
}
将文件上传到 Google 驱动器
_uploadFileToGoogleDrive() async {
var client = GoogleHttpClient(await googleSignInAccount.authHeaders);
var drive = ga.DriveApi(client);
ga.File fileToUpload = ga.File();
var file = await FilePicker.getFile();
fileToUpload.parents = ["appDataFolder"];
fileToUpload.name = path.basename(file.absolute.path);
var response = await drive.files.create(
fileToUpload,
uploadMedia: ga.Media(file.openRead(), file.lengthSync()),
);
print(response);
}
下载Google 驱动器文件
Future<void> _downloadGoogleDriveFile(String fName, String gdID) async {
var client = GoogleHttpClient(await googleSignInAccount.authHeaders);
var drive = ga.DriveApi(client);
ga.Media file = await drive.files
.get(gdID, downloadOptions: ga.DownloadOptions.FullMedia);
print(file.stream);
final directory = await getExternalStorageDirectory();
print(directory.path);
final saveFile = File('${directory.path}/${new DateTime.now().millisecondsSinceEpoch}$fName');
List<int> dataStore = [];
file.stream.listen((data) {
print("DataReceived: ${data.length}");
dataStore.insertAll(dataStore.length, data);
}, onDone: () {
print("Task Done");
saveFile.writeAsBytes(dataStore);
print("File saved at ${saveFile.path}");
}, onError: (error) {
print("Some Error");
});
}
这很简单,但您必须通过一系列步骤才能完成它。
1.启用 Google 驱动器 API
点击 link 到 google 控制台: https://console.developers.google.com/apis/library?project=diary-app-339509
- 创建一个新项目并在该项目上启用 Drive API。
- 为 android 和 IOS 创建一个 OAuth 客户端 ID
- 创建它需要填写很多详细信息,通读页面并填写
- 复制客户端 ID 以备将来使用
create project and enable API
Create OAuth client id
form to fill to make OAuth client id
2。使项目可供外部使用。
for testing add test user email address
Flutter 上的编码部分
3。将以下包添加到 pubspec.yaml
googleapis: ^7.0.0
googleapis_auth: ^1.3.0
flutter_secure_storage: ^5.0.2
url_launcher: ^6.0.0-nullsafety
4。从 google 获取身份验证并存储身份验证详细信息以供将来进行身份验证
使用以下 class 存储授权详细信息
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:googleapis_auth/auth_io.dart';
class SecureStorage {
final storage = FlutterSecureStorage();
//Save Credentials
Future saveCredentials(AccessToken token, String refreshToken) async {
print(token.expiry.toIso8601String());
await storage.write(key: "type", value: token.type);
await storage.write(key: "data", value: token.data);
await storage.write(key: "expiry", value: token.expiry.toString());
await storage.write(key: "refreshToken", value: refreshToken);
}
//Get Saved Credentials
Future<Map<String, dynamic>?> getCredentials() async {
var result = await storage.readAll();
if (result.isEmpty) return null;
return result;
}
//Clear Saved Credentials
Future clear() {
return storage.deleteAll();
}
}
5.Finally 使用此 class 将文件保存到云端硬盘
import 'dart:io';
import 'package:googleapis/drive/v3.dart' as ga;
import 'package:googleapis_auth/auth_io.dart';
import 'package:http/http.dart' as http;
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'package:personal_diary/app/utils/secure_storage.dart';
import 'package:url_launcher/url_launcher.dart';
const _clientId = "379503391554-6d33j9e6lan7lg0ac7sj81026u8nigs8.apps.googleusercontent.com";
const _scopes = ['https://www.googleapis.com/auth/drive.file'];
class GoogleDrive {
final storage = SecureStorage();
//Get Authenticated Http Client
Future<http.Client> getHttpClient() async {
//Get Credentials
var credentials = await storage.getCredentials();
if (credentials == null) {
//Needs user authentication
var authClient = await clientViaUserConsent(
ClientId(_clientId),_scopes, (url) {
//Open Url in Browser
launch(url);
});
//Save Credentials
await storage.saveCredentials(authClient.credentials.accessToken,
authClient.credentials.refreshToken!);
return authClient;
} else {
print(credentials["expiry"]);
//Already authenticated
return authenticatedClient(
http.Client(),
AccessCredentials(
AccessToken(credentials["type"], credentials["data"],
DateTime.tryParse(credentials["expiry"])!),
credentials["refreshToken"],
_scopes));
}
}
// check if the directory forlder is already available in drive , if available return its id
// if not available create a folder in drive and return id
// if not able to create id then it means user authetication has failed
Future<String?> _getFolderId(ga.DriveApi driveApi) async {
final mimeType = "application/vnd.google-apps.folder";
String folderName = "personalDiaryBackup";
try {
final found = await driveApi.files.list(
q: "mimeType = '$mimeType' and name = '$folderName'",
$fields: "files(id, name)",
);
final files = found.files;
if (files == null) {
print("Sign-in first Error");
return null;
}
// The folder already exists
if (files.isNotEmpty) {
return files.first.id;
}
// Create a folder
ga.File folder = ga.File();
folder.name = folderName;
folder.mimeType = mimeType;
final folderCreation = await driveApi.files.create(folder);
print("Folder ID: ${folderCreation.id}");
return folderCreation.id;
} catch (e) {
print(e);
return null;
}
}
uploadFileToGoogleDrive(File file) async {
var client = await getHttpClient();
var drive = ga.DriveApi(client);
String? folderId = await _getFolderId(drive);
if(folderId == null){
print("Sign-in first Error");
}else {
ga.File fileToUpload = ga.File();
fileToUpload.parents = [folderId];
fileToUpload.name = p.basename(file.absolute.path);
var response = await drive.files.create(
fileToUpload,
uploadMedia: ga.Media(file.openRead(), file.lengthSync()),
);
print(response);
}
}
Future<void> _downloadGoogleDriveFile(String fName, String gdID) async {
var client = await getHttpClient();
var drive = ga.DriveApi(client);
ga.Media file = await drive.files.get(gdID, downloadOptions: ga.DownloadOptions.FullMedia);
print(file.stream);
final directory = await getExternalStorageDirectory();
print(directory);
final saveFile = File('$directory/${DateTime.now().millisecondsSinceEpoch}$fName');
List<int> dataStore = [];
file.stream.listen((data) {
print("DataReceived: ${data.length}");
dataStore.insertAll(dataStore.length, data);
}, onDone: () {
print("Task Done");
saveFile.writeAsBytes(dataStore);
print("File saved at ${saveFile.path}");
}, onError: (error) {
print("Some Error");
});
}
}