12

I am making a Flutter mobile app and am trying to save images taken with the camera to a specific Google drive account for storage. I have obtained a service account JSON file from my Google account to authenticate.

This is the code I am using:

import 'dart:io' as io;
import 'package:googleapis/drive/v3.dart' as ga;
import 'package:googleapis_auth/auth.dart';
import 'package:googleapis_auth/auth_io.dart';

uploadFileToGoogleDrive(io.File fileToUpload) async {

  // send authorization to access Google Drive
  // from https://pub.dev/packages/googleapis_auth

  ServiceAccountCredentials accountCredentials = new ServiceAccountCredentials.fromJson({
//my service account JSON information goes here
  });
  List<String> scopes = [ga.DriveApi.DriveFileScope, ga.DriveApi.DriveScope];


 
    try {
      // authenticates with Google Drive with our service account credentials
      AuthClient client = await clientViaServiceAccount(
          accountCredentials, scopes);

      ga.DriveApi drive = ga.DriveApi(client);
      ga.File file = ga.File(); // create new file record to upload to Drive
      try {
        file.name = 'image.jpg';
        
        ga.File response = await drive.files.create(file, uploadMedia: ga.Media(
            fileToUpload.openRead(), fileToUpload.lengthSync()));
   
        print('uploaded with a file size of: ${response.size}');
      } on Exception catch (e) {
        print('upload error: $e');
      }
      client.close();
    } on Exception catch (e) {
      print('credentials error: $e');
    }
  
}

The file I am uploading has a size of 24095. No error messages are returned. The returned 'response' has a Google drive file ID but a size of null, and I cannot the uploaded file in the Google Drive when logging in from the web. What is the correct way to upload this file using the Google Drive API?

4
  • why google drive ? why not google storage / Amazon web service S3 ? Commented Jan 19, 2021 at 4:38
  • 1
    Have you checked the googleapis example on how to upload and download? Commented Jan 19, 2021 at 8:58
  • @dwikurniantom I had thought that Google Drive would have been easier since I was more familiar with it, but it doesn't seem to be so I'll look into those alternatives. Thanks! Commented Jan 19, 2021 at 23:57
  • @Kessy Thanks! I had been following some tutorials on Medium but hadn't found that one. Commented Jan 19, 2021 at 23:58

3 Answers 3

21

Its easy but you have to go through a series of Steps to complete it.

1. Enable Google Drive API

click on the link to google console : https://console.developers.google.com/apis/library?project=diary-app-339509

  • Create a new project and enable Drive API on the project.
  • Create an OAuth client Id for android and IOS
  • You will have to fill a lot of details to create it , read through the page and fill it
  • copy Client ID for future use

create project and enable API

Create OAuth client id

form to fill to make OAuth client id

2. Make project available for external use .

for testing add test user email address

Now comes the coding part on flutter

3. Add the following packages to pubspec.yaml

  googleapis: ^7.0.0
  googleapis_auth: ^1.3.0
  flutter_secure_storage: ^5.0.2
  url_launcher: ^6.0.0-nullsafety

4. Get authentication from google and store the Auth details for future auths

Use the following class for storing Auth Details

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 use this class for saving your file to Drive

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:personal_diary/app/utils/secure_storage.dart';
import 'package:url_launcher/url_launcher.dart';

const _clientId = "YOUR CLIENT ID FROM GOOGLE CONSOLE";
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);
    }

  }




}
Sign up to request clarification or add additional context in comments.

1 Comment

Does this solution avoid Google login? Or the user still needs to login with a Google account in order to be able to upload the file?
1

Turns out in addition to enabling the API and creating the service account, I ALSO needed to share the folder I wanted to upload to with the service account email address from the JSON.

Comments

0

I got this error recently and I tried hard and finally I solved it. The error is due to SHA-1 key mismatch. Please go to your Android directory and use the ./gradlew signingReport => command in it.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.