Dart, Flutter: upload .jpg file to Google Drive - dart

I am trying to upload some pictures taken in my application to Google Drive.
I created a project and a Service account key and I have Google Drive API enabled.
My code snippet looks like this:
// sfilePath = "/storage/emulated/0/Xyz";
// sfileName = "PIC_17";
final accountCredentials = new ServiceAccountCredentials.fromJson({
"private_key_id": "...",
"private_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n",
"client_email": "...#...gserviceaccount.com",
"client_id": "...apps.googleusercontent.com",
"type": "service_account"
});
AuthClient client = await clientViaServiceAccount(
accountCredentials,
['https://www.googleapis.com/auth/drive']);
var driveApi = ga.DriveApi(client);
var fileToUpload = File(sfilePath + sfileName + '.jpg');
var response = await driveApi.files.create(ga.File()..name = 'my_file.jpg',
uploadMedia: ga.Media(fileToUpload.openRead(), fileToUpload.lengthSync()));
print(response.toJson());
After running the program
print(response.toJson())
results in:
{id: ..., kind: drive#file, mimeType: image/jpeg, name: my_file.jpg}
and I see the Requests number for Google Drive API on Dashboard increasing after a while, but I can't find my_file.jpg on my Google Drive.
What could be the problem? How could I test what happens, what is the exact response?
My solution is from: Dart: upload .jpg file to google Drive
Thanks,
IMozes

I think the problem is that you are using a Service Account.
When using Service Account Key the Apis are working but your file is not stored in YOUR Google Drive but in a separated Google Drive.
To check this you can add the following code
var fileList = await driveApi.files.list();
fileList.files.forEach((file) => print('---${file.name})'));
Using Service Account Key, you will have only one file named "welcome..".
The solution is to use Firebase Authentication with Google API Key with a code like:
final FirebaseAuth _auth = FirebaseAuth.instance;
final GoogleSignIn googleSignIn = GoogleSignIn(
scopes: ['https://www.googleapis.com/auth/drive'],
);
final GoogleSignInAccount googleSignInAccount = await
googleSignIn.signIn();
final GoogleSignInAuthentication googleSignInAuthentication =
await googleSignInAccount.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
accessToken: googleSignInAuthentication.accessToken,
idToken: googleSignInAuthentication.idToken,
);
AuthResult authResult = await _auth.signInWithCredential(credential);
var client = GoogleHttpClient(await googleSignInAccount.authHeaders);
var driveApi = drive.DriveApi(client);
Where GoogleHttpClient is defined like here https://stackoverflow.com/a/48485898/8676527

You must share your drive folder where you upload image with the client_email of credential

Related

How create a Signed URL to upload Files in Google Cloud Storage using Dart

I'm trying to create an API in Dart to generate a Signed URL to upload a file in Cloud Storage but even following the google documentation I can't successfully generate. In all my attempts, when making the request the API returns me: The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.
I think the error is in generating the signature, or in the hash of the canonical request
final canonicalRequest = getCanonicalRequest(
canonicalUri,
canonicalQueryString,
canonicalHeaders,
signedHeaders,
);
final bytes = utf8.encode(canonicalRequest);
final hashedCanonicalRequest = HEX.encode(sha256.convert(bytes).bytes);
final stringToSign = getStringToSign(
dateTimestamp,
credentialScope,
hashedCanonicalRequest,
);
final signature = await getSignature(stringToSign);
Future<String> getSignature(String stringToSign) async {
final privateKey =
await parseKeyFromFile<pointy.RSAPrivateKey>('private.pem');
final signer =
Signer(RSASigner(RSASignDigest.SHA256, privateKey: privateKey));
final signature = signer.sign(stringToSign);
return HEX.encode(signature.bytes);
}

YouTube Data API: Just started giving back 403 forbidden errors when trying to update videos even though OAuth is set up correctly

I am receiving the following error when trying to update video snippets using YouTube API using C#:
Google.GoogleApiException: Google.Apis.Requests.RequestError
Forbidden [403]
Errors [
Message[Forbidden] Location[ - ] Reason[forbidden] Domain[youtube.video]
]
However, I can read videos just fine.
I'm using oauth and have granted proper access to my app. I've tried recreating my oauth credentials and re-granting access but to no avail.
This is something that was working previously. No change to my code. No change to my channel or videos. I've also verified no quota limits exceeded for the day.
The code:
private async Task<UserCredential> GetCredentialAsync()
{
UserCredential credential;
using (var stream = new FileStream(<<path to json file containing my oauth credentials>>, FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
new[] { YouTubeService.Scope.YoutubeForceSsl, YouTubeService.Scope.Youtube, YouTubeService.Scope.YoutubeUpload },
"user",
CancellationToken.None,
new FileDataStore(appName)
);
}
return credential;
}
private async Task<bool> UpdateSingleVideoAsync(string id, VideoSnippet snippet)
{
var credential = this.GetCredentialAsync().Result;
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = this.GetType().ToString()
});
var video = new Video();
video.Id = id;
video.Snippet = snippet;
var request = youtubeService.Videos.Update(video, "snippet");
var response = await request.ExecuteAsync();
return true;
}
Any pointers on getting more data about the problem?

copying google doc file in shared drive using c#

We have been using google drive for a while now to select an existing doc, owned by one of our employees, copy it and then merge data into placeholder fields to then download a pdf version of the doc. It's been working fine except now other employees want access to the created docs. So when we give them a link, the original employee has to give them access. We want to move the docs to a shared drive where all employees can see anything in the shared drive. From what I could find in google on this, it looks like we need to set the SupportsAllDrives property to true on the request. However, I can't find that property on any of the objects that we're creating when copying the base document. Because of this I keep getting a 404 from google when trying to copy the file. Can anyone suggest how to get this working?
var secrets = new ClientSecrets
{
ClientId = GoogleCredentials.accesskey,
ClientSecret = GoogleCredentials.secretkey
};
var refreshToken = _credService.GetRefreshToken();
if (string.IsNullOrEmpty(refreshToken))
{
throw new Exception("Missing google refresh token for google doc processor task.");
}
var token = new TokenResponse { RefreshToken = refreshToken };
var credentials = new UserCredential(new GoogleAuthorizationCodeFlow(
new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = secrets
}),
"user",
token);
var docService = new DocsService(new BaseClientService.Initializer
{
HttpClientInitializer = credentials,
ApplicationName = "Contract Merge"
});
var driveService = new DriveService(new BaseClientService.Initializer
{
HttpClientInitializer = credentials,
ApplicationName = "Contract Merge"
});
var newTitle = "Agreement for " + contract.FirstName + " " + contract.LastName + " " + DateTime.Now.Month.ToString() + "-" + DateTime.Now.Day.ToString() + "-" + DateTime.Now.Year.ToString();
var newFile = new google.Apis.Drive.v2.Data.File { Title = newTitle };
var documentCopyFile = driveService.Files.Copy(newFile, GoogleConstants.TemplateDocId).Execute();
As you can see in the C# library reference, an optional query parameter like supportsAllDrives (see query parameters on the API docs) is handled by a property of the CopyRequest class.
Therefore, after building the CopyRequest, but before executing it, you have to set the property SupportsAllDrives to true, as shown here:
FilesResource.CopyRequest copyRequest = driveService.Files.Copy(newFile, GoogleConstants.TemplateDocId);
copyRequest.SupportsAllDrives = true;
copyRequest.Execute();
Reference:
Class FilesResource.CopyRequest

Spreadsheets API Can not update a read-only feed

I'm trying to add new spreadsheet if it not exists with GData Spreadsheet API for .NET but it gives me following exception:
Can not update a read-only feed
Here's my code:
var service = new SpreadsheetsService("<my-app>");
service.setUserCredentials("<login>", "<password>");
// Instantiate a SpreadsheetQuery object to retrieve spreadsheets.
SpreadsheetQuery query = new SpreadsheetQuery();
var title = "test";
query.Title = title;
// Make a request to the API and get all spreadsheets.
SpreadsheetFeed feed = service.Query(query);
if (!feed.Entries.Any())
{
var worksheet = new WorksheetEntry(20, 20, title);
service.Insert(feed, worksheet);
}
Through Fiddler I see that I'm doing request to:
GET /feeds/spreadsheets/private/full?title=test
and it goes fine, but I don't see any requests for updating data. I suppose that I should change somehow SpreadsheetQuery to make it capable to write data, but I can't find how.
It's me being inattentive because google documentation on Spreadsheet API says:
It is possible to create a new spreadsheet by uploading a spreadsheet
file via the Google Drive API. The Spreadsheets API does not currently
provide a way to delete a spreadsheet, but this is also provided in
the Google Drive API. For testing purposes, you may create a
spreadsheet manually or upload one.
So I basically installed GoogleDrive API with Nuget. And then added following method for adding file:
private static void AddFile(string title)
{
var clientID = "put here a clientID";
var clientSecret = "put here a clientSecret";
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = clientID,
ClientSecret = clientSecret,
},
new[] { DriveService.Scope.Drive },
"here goes your account",
CancellationToken.None).Result;
// Create the service.
var service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Drive API Sample",
});
var body = new Google.Apis.Drive.v2.Data.File();
body.Title = title;
//body.Description = "A test document";
body.MimeType = "application/vnd.google-apps.spreadsheet";
service.Files.Insert(body).Execute();
}
When I run code above at the first time - I received an exception that said
Could not load file or assembly
'Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.16.0
I run these in Package Manager Console:
Uninstall-Package Microsoft.Bcl.Async -Force
Install-Package Microsoft.Bcl.Async
and it worked. Hope it would help somebody who will stumble over the same issue.

OAuth error when exporting Sheet as XLS in Google Apps Script

I had a Google Apps Script to take appointments from my Google Calendar, copy them into a Google Sheet, convert it to XLS and email it. It was working fine until this week.
The initial problem was a 302 error, probably caused by the new version of Sheets. This has been discussed here: Export (or print) with a google script new version of google spreadsheets to pdf file, using pdf options
I got the new location of the file by muting the HTTP exceptions and adjusting the URL accordingly. I also updated the OAuth scope to https://docs.google.com/feeds/ as suggested.
The program is failing with an "OAuth error" message. When muteHttpExceptions is set to true, the message is "Failed to authenticate to service: google".
I guess this is a scope problem but I really can't see what I've done wrong. Naturally, I've tried a few other possibilities without luck.
I've included the code below. Commented code is the instruction that worked until this week.
function getSSAsExcel(ssID)
{
var format = "xls";
//var scope = "https://spreadsheets.google.com/feeds/";
var scope = "https://docs.google.com/feeds/";
var oauthConfig = UrlFetchApp.addOAuthService("google");
oauthConfig.setAccessTokenUrl("https://www.google.com/accounts/OAuthGetAccessToken");
oauthConfig.setRequestTokenUrl("https://www.google.com/accounts/OAuthGetRequestToken?scope=" + scope);
oauthConfig.setAuthorizationUrl("https://www.google.com/accounts/OAuthAuthorizeToken");
oauthConfig.setConsumerKey("anonymous");
oauthConfig.setConsumerSecret("anonymous");
var requestData = {
//"muteHttpExceptions": true,
"method": "GET",
"oAuthServiceName": "google",
"oAuthUseToken": "always"
};
//var url = "https://spreadsheets.google.com/feeds/download/spreadsheets/Export?key=" + ssID
var url = "https://docs.google.com/spreadsheets/d/" + ssID
+ "/feeds/download/spreadsheets/Export?"
+ "&size=A4" + "&portrait=true" +"&fitw=true" + "&exportFormat=" + format;
var result = UrlFetchApp.fetch(url , requestData);
var contents = result.getContent();
return contents;
}
Thanks for your help!
Instead of using OAuthConfig (which must be auth'ed in the Script Editor) you can pass an OAuth2 token instead, retrievable via ScriptApp.getOAuthToken().
The code snippet below uses the Advanced Drive service to get the export URL, but if you hand construct the URL you'll need to ensure that the Drive scope is still requested by your script (simply include a call to DriveApp.getRootFolder() somewhere in your script code).
function exportAsExcel(spreadsheetId) {
var file = Drive.Files.get(spreadsheetId);
var url = file.exportLinks[MimeType.MICROSOFT_EXCEL];
var token = ScriptApp.getOAuthToken();
var response = UrlFetchApp.fetch(url, {
headers: {
'Authorization': 'Bearer ' + token
}
});
return response.getBlob();
}

Resources