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");
        });
      }
    
    }