Google 드라이브 앱 데이터 백업

앱 데이터 백업은 모바일 앱의 일반적인 작업입니다. 이 문서에서는 Android 및 IOS 파일을 Google 드라이브에 백업하는 단계를 보여줍니다.

구글 로그인



앱 데이터 읽기 및 쓰기 범위에는 Google 계정 로그인이 필요합니다. 세부 구현은 를 참조할 수 있습니다.

드라이브 API 사용



사이드 메뉴에서 Enabled APIs & Services로 이동하여 + ENABLE APIS AND SERVICES를 클릭합니다.



검색 google drive api .





활성화Google Drive API를 클릭합니다.



Flutter 프로젝트 구성


flutter pub add googleapis를 실행하여 googleapis 플러그인을 프로젝트에 추가합니다.
GoogleDriveAppData 라는 클래스를 만듭니다. 로그인 논리도 이 클래스에 포함됩니다. Google 드라이브의 앱 데이터 저장소를 사용할 것이므로 범위를 drive.DriveApi.driveAppdataScope 에 추가합니다.

import 'package:google_sign_in/google_sign_in.dart';
import 'package:googleapis/drive/v3.dart' as drive;

class GoogleDriveAppData {
  /// sign in with google
  Future<GoogleSignInAccount?> signInGoogle() async {
    GoogleSignInAccount? googleUser;
    try {
      GoogleSignIn googleSignIn = GoogleSignIn(
        scopes: [
          drive.DriveApi.driveAppdataScope,
        ],
      );

      googleUser =
          await googleSignIn.signInSilently() ?? await googleSignIn.signIn();
    } catch (e) {
      debugPrint(e.toString());
    }
    return googleUser;
  }

  ///sign out from google
  Future<void> signOut() async {
    GoogleSignIn googleSignIn = GoogleSignIn();
    await googleSignIn.signOut();
  }
}


드라이브 API를 클라이언트와 함께 사용하기 위해 드라이브 API 요청과 함께 인증 정보를 전달하기 위한 GoogleAuthClient 클래스를 생성합니다.

실행flutter pub add http하여 http 패키지를 가져옵니다.

import 'package:http/http.dart' as http;

class GoogleAuthClient extends http.BaseClient {
  final Map<String, String> _headers;
  final _client = http.Client();

  GoogleAuthClient(this._headers);

  @override
  Future<http.StreamedResponse> send(http.BaseRequest request) {
    request.headers.addAll(_headers);
    return _client.send(request);
  }
}

GoogleDriveAppData 클래스로 돌아갑니다.

드라이브 클라이언트 인스턴스를 생성하는 기능을 추가합니다.

import 'package:google_drive_app_data/google_auth_client.dart';

class GoogleDriveAppData {
    ...
    ///get google drive client
  Future<drive.DriveApi?> getDriveApi(GoogleSignInAccount googleUser) async {
    drive.DriveApi? driveApi;
    try {
      Map<String, String> headers = await googleUser.authHeaders;
      GoogleAuthClient client = GoogleAuthClient(headers);
      driveApi = drive.DriveApi(client);
    } catch (e) {
      debugPrint(e.toString());
    }
    return driveApi;
  }
}


Google 드라이브에는 특히 앱의 파일 저장을 위한 앱 데이터 폴더가 있습니다. 파일이 appDataFolder에 저장됩니다.

파일의 기본 이름을 쉽게 가져오기 위해 경로 패키지를 추가하려면 실행flutter pub add path하십시오.
drive.File는 드라이브가 가지고 있는 파일 정보를 저장하고 있습니다.
io.File는 장치가 가지고 있는 로컬 파일인 io 파일입니다.

파일이 이전에 드라이브에 업로드된 경우 driveFileId를 사용하여 드라이브에 최신 버전 파일을 업데이트할 수 있습니다.

import 'dart:io' as io;

import 'package:path/path.dart' as path;

class GoogleDriveAppData {
    ...
    /// upload file to google drive
  Future<drive.File?> uploadDriveFile({
    required drive.DriveApi driveApi,
    required io.File file,
    String? driveFileId,
  }) async {
    try {
      drive.File fileMetadata = drive.File();
      fileMetadata.name = path.basename(file.absolute.path);

      late drive.File response;
      if (driveFileId != null) {
        /// [driveFileId] not null means we want to update existing file
        response = await driveApi.files.update(
          fileMetadata,
          driveFileId,
          uploadMedia: drive.Media(file.openRead(), file.lengthSync()),
        );
      } else {
        /// [driveFileId] is null means we want to create new file
        fileMetadata.parents = ['appDataFolder'];
        response = await driveApi.files.create(
          fileMetadata,
          uploadMedia: drive.Media(file.openRead(), file.lengthSync()),
        );
      }
      return response;
    } catch (e) {
      debugPrint(e.toString());
      return null;
    }
  }
}


ID, 버전 및 수정 시간과 같은 드라이브의 파일 정보를 얻으려면 다음 기능을 사용하여 파일 목록을 가져오고 파일 이름을 업로드된 파일과 비교할 수 있습니다.

class GoogleDriveAppData {
    ...
    /// get drive file info
  Future<drive.File?> getDriveFile(
      drive.DriveApi driveApi, String filename) async {
    try {
      drive.FileList fileList = await driveApi.files.list(
          spaces: 'appDataFolder', $fields: 'files(id, name, modifiedTime)');
      List<drive.File>? files = fileList.files;
      drive.File? driveFile =
          files?.firstWhere((element) => element.name == filename);
      return driveFile;
    } catch (e) {
      debugPrint(e.toString());
      return null;
    }
  }
}


드라이브 파일을 로컬로 다운로드하려면 다음 기능을 사용할 수 있습니다. 기본적으로 데이터 스트림을 다운로드하고 새 파일에 씁니다.

class GoogleDriveAppData {
    ...
    /// download file from google drive
  Future<io.File?> restoreDriveFile({
    required drive.DriveApi driveApi,
    required drive.File driveFile,
    required String targetLocalPath,
  }) async {
    try {
      drive.Media media = await driveApi.files.get(driveFile.id!,
          downloadOptions: drive.DownloadOptions.fullMedia) as drive.Media;

      List<int> dataStore = [];

      await media.stream.forEach((element) {
        dataStore.addAll(element);
      });

      io.File file = io.File(targetLocalPath);
      file.writeAsBytesSync(dataStore);

      return file;
    } catch (e) {
      debugPrint(e.toString());
      return null;
    }
  }
}


전체GoogleDriveAppData 클래스는 다음과 같습니다.

import 'dart:io' as io;

import 'package:flutter/foundation.dart';
import 'package:google_drive_app_data/google_auth_client.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:googleapis/drive/v3.dart' as drive;
import 'package:path/path.dart' as path;

class GoogleDriveAppData {
  /// sign in with google
  Future<GoogleSignInAccount?> signInGoogle() async {
    GoogleSignInAccount? googleUser;
    try {
      GoogleSignIn googleSignIn = GoogleSignIn(
        scopes: [
          drive.DriveApi.driveAppdataScope,
        ],
      );

      googleUser =
          await googleSignIn.signInSilently() ?? await googleSignIn.signIn();
    } catch (e) {
      debugPrint(e.toString());
    }
    return googleUser;
  }

  ///sign out from google
  Future<void> signOut() async {
    GoogleSignIn googleSignIn = GoogleSignIn();
    await googleSignIn.signOut();
  }

  ///get google drive client
  Future<drive.DriveApi?> getDriveApi(GoogleSignInAccount googleUser) async {
    drive.DriveApi? driveApi;
    try {
      Map<String, String> headers = await googleUser.authHeaders;
      GoogleAuthClient client = GoogleAuthClient(headers);
      driveApi = drive.DriveApi(client);
    } catch (e) {
      debugPrint(e.toString());
    }
    return driveApi;
  }

  /// upload file to google drive
  Future<drive.File?> uploadDriveFile({
    required drive.DriveApi driveApi,
    required io.File file,
    String? driveFileId,
  }) async {
    try {
      drive.File fileMetadata = drive.File();
      fileMetadata.name = path.basename(file.absolute.path);

      late drive.File response;
      if (driveFileId != null) {
        /// [driveFileId] not null means we want to update existing file
        response = await driveApi.files.update(
          fileMetadata,
          driveFileId,
          uploadMedia: drive.Media(file.openRead(), file.lengthSync()),
        );
      } else {
        /// [driveFileId] is null means we want to create new file
        fileMetadata.parents = ['appDataFolder'];
        response = await driveApi.files.create(
          fileMetadata,
          uploadMedia: drive.Media(file.openRead(), file.lengthSync()),
        );
      }
      return response;
    } catch (e) {
      debugPrint(e.toString());
      return null;
    }
  }

  /// download file from google drive
  Future<io.File?> restoreDriveFile({
    required drive.DriveApi driveApi,
    required drive.File driveFile,
    required String targetLocalPath,
  }) async {
    try {
      drive.Media media = await driveApi.files.get(driveFile.id!,
          downloadOptions: drive.DownloadOptions.fullMedia) as drive.Media;

      List<int> dataStore = [];

      await media.stream.forEach((element) {
        dataStore.addAll(element);
      });

      io.File file = io.File(targetLocalPath);
      file.writeAsBytesSync(dataStore);

      return file;
    } catch (e) {
      debugPrint(e.toString());
      return null;
    }
  }

  /// get drive file info
  Future<drive.File?> getDriveFile(
      drive.DriveApi driveApi, String filename) async {
    try {
      drive.FileList fileList = await driveApi.files.list(
          spaces: 'appDataFolder', $fields: 'files(id, name, modifiedTime)');
      List<drive.File>? files = fileList.files;
      drive.File? driveFile =
          files?.firstWhere((element) => element.name == filename);
      return driveFile;
    } catch (e) {
      debugPrint(e.toString());
      return null;
    }
  }
}


결과



로그인으로 간단한 위젯을 만들고 업로드 기능을 호출해 봅시다.

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final GoogleDriveAppData _googleDriveAppData = GoogleDriveAppData();
  GoogleSignInAccount? _googleUser;
  drive.DriveApi? _driveApi;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            GoogleAuthButton(
              onPressed: () async {
                if (_googleUser == null) {
                  _googleUser = await _googleDriveAppData.signInGoogle();
                  if (_googleUser != null) {
                    _driveApi =
                        await _googleDriveAppData.getDriveApi(_googleUser!);
                  }
                } else {
                  await _googleDriveAppData.signOut();
                  _googleUser = null;
                  _driveApi = null;
                }
                setState(() {});
              },
            ),
            ElevatedButton(
              onPressed: _driveApi != null
                  ? () {
                      FilePicker.platform.pickFiles().then((value) {
                        if (value != null && value.files[0] != null) {
                          File selectedFile = File(value.files[0].path!);
                          _googleDriveAppData.uploadDriveFile(
                            driveApi: _driveApi!,
                            file: selectedFile,
                          );
                        }
                      });
                    }
                  : null,
              child: Text('Save sth to drive'),
            ),
          ],
        ),
      ),
    );
  }
}




업로드 성공 후 드라이브 웹 포털로 이동하여 설정을 클릭하면.



사이드바에서 Manage Apps를 선택하면 앱 목록에 앱이 나타납니다.



참고: 이전 기사에서 한 번 로그인한 경우 계정에 드라이브 앱 데이터 권한이 포함되어 있지 않은 경우 파일을 업로드할 때 403이 발생할 수 있습니다. 계정에서 앱 데이터 권한을 허용하려면 한 번 로그아웃했다가 로그인하거나 다른 방법으로 계정을 허용해야 합니다.

내용이 마음에 드셨다면 지원해주세요🍖


연결🍻


좋은 웹페이지 즐겨찾기