【问题标题】:Flutter: How to backup user data on Google drive like WhatsApp does?Flutter:如何像 WhatsApp 一样在 Google Drive 上备份用户数据?
【发布时间】:2021-08-27 14:57:21
【问题描述】:

我想在用户 Google Drive 帐户的 Backups 中备份数据并恢复它们。

我见过像 WhatsApp 这样的应用程序,它们允许用户通过 google drive 登录并定期备份到用户云。

我不想使用 firebase 云,因为数据是由用户自己而不是其他用户访问的,如果数据很大,成本会很高。有没有可用的软件包可以做到这一点?或者教程如何在颤振中实现这一点?

【问题讨论】:

    标签: flutter google-drive-api backup


    【解决方案1】:

    第 1 步

    您需要已经创建了一个 Google Firebase 项目,并从 Google Developer Console 中启用 Google Drive API。请注意,您需要在 Google Developer Console 中选择与在 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");  
       });  
     } 
    

    【讨论】:

      【解决方案2】:

      这很容易,但您必须经过一系列步骤才能完成。

      1.启用 Google Drive API

      点击链接到谷歌控制台: 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 获取身份验证并存储身份验证详细信息以供将来身份验证使用

      使用以下类来存储 Auth 详细信息

      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.最后使用此类将文件保存到云端硬盘

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

      【讨论】:

      • 感谢@Jeslin 的回答。但这会将用户带到外部浏览器屏幕,并且即使在身份验证后也停留在那里。是否可以进行某种重定向以将用户带回应用程序?
      猜你喜欢
      • 2021-02-19
      • 1970-01-01
      • 2020-04-08
      • 1970-01-01
      • 1970-01-01
      • 2019-06-06
      • 1970-01-01
      • 1970-01-01
      • 2017-10-17
      相关资源
      最近更新 更多