How do I upload a file to Google Drive using Flutter?
Solution 1
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
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);
}
}
}
Solution 2
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.
Aaron Whitfield
Updated on December 27, 2022Comments
-
Aaron Whitfield over 1 year
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?
-
Dwi Kurnianto M about 3 yearswhy google drive ? why not google storage / Amazon web service S3 ?
-
Kessy about 3 yearsHave you checked the googleapis example on how to upload and download?
-
Aaron Whitfield about 3 years@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!
-
Aaron Whitfield about 3 years@Kessy Thanks! I had been following some tutorials on Medium but hadn't found that one.
-