In this post, we will check how we can Upload File to Google Drive Using Flow and Named Credentials. In our previous post we have upload using oAuth 2.0 flow. But this flow break when we use that component on record detail page during authentication, as Salesforce remove parameters from URL in record details page.
So to overcome that we will be using Named Credentials to do the authentication. So after completing this post we will learn below things:
- Pass record id in Screen Flow from Lightning record detail page.
- Use Named credentials to do authentication to Google API.
- Make multipart request to Google Drive API and pass file metadata.
- Reset collection variable in flow.
Firstly, we need to create an App and get access token, you can follow same steps from my previous post to complete this and get app credentials.
Create Authentication Provider
Firstly we need to create Authentication Provider. So use below details.
Name: Google API
Consumer Key: Enter key which you got from google API developer console
Consumer Secret: Enter Secret which you got from google API developer console
Authorize Endpoint URL: https://accounts.google.com/o/oauth2/auth?access_type=offline&approval_prompt=force
Token Endpoint URL: https://accounts.google.com/o/oauth2/token
Default Scopes: https://www.googleapis.com/auth/drive
Leave other value as default and save the Auth. provider.

Once you save the Auth Provider, you will get the Callback URL, Copy this URL and add this to the google API callback URL list.



Create Named Credentials
Secondly we need to create Named Credentials. For that use below details.
Label: Drive
URL: https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart
Identity Type: Per User
Authentication Protocol: OAuth 2.0
Authentication Provider: Select The authentication Provider we created in last step
Scope: https://www.googleapis.com/auth/drive



As we are using Per User Identity type. Therefore, we don’t need to run the AUTH Flow here. So now go to user Setting > Select Authentication Setting for External System and Select your user and Check Start Authentication Flow on Save and run the App.



So now we have completed the Authentication part. Most importantly, we don’t need to worry about refresh token as Salesforce take care of that.
File to Google Drive Using Flow
Finally we need to create Flow to Upload File to Google Drive. Firstly, In Screen Flow Drag the File selector Component. After that in next screen call the Apex method and Pass the parameter.
Once we get the response from Apex we store them in variable. After that based on the flag we display message to user. Finally after success we clear the file Id list. So we accidently don’t pass the same file again.















We have used below Apex class to make the request.
GoogleDriveAPIController.Apxc
public with sharing class GoogleDriveAPIController {
@InvocableMethod(label='Upload File to Google Drive' description='Upload File to Google Drive')
public static List<ResponseWrapper> storeFiletoDrive(List<List<ID>> cdIdList) {
system.debug('========='+cdIdList);
ResponseWrapper rw = new ResponseWrapper();
if(cdIdList.isEmpty() || (!cdIdList.isEmpty() && cdIdList[0].isEmpty() ) ) {
rw.isSuccess = False;
rw.response = 'Please select file.';
return new List<ResponseWrapper>{rw};
}
List<ContentVersion> cvFile = [SELECT Id, ContentDocumentId, VersionData, FileType, ContentDocument.Title FROM ContentVersion WHERE ContentDocumentId =: cdIdList[0] ];
String boundary = 'SalesforceNewsTechnologyStuff';
String delimiter = '\r\n--' + boundary + '\r\n';
String close_delim = '\r\n--' + boundary + '--';
system.debug('===================conver.VersionData======================='+cvFile[0].VersionData);
String bodyEncoded = EncodingUtil.base64Encode( cvFile[0].VersionData);
String body = delimiter + 'Content-Type: application/json\r\n\r\n' + '{ "name" : "' + cvFile[0].ContentDocument.Title + '",' + ' "mimeType" : "image/' + cvFile[0].FileType + '"}' + delimiter + 'Content-Type: image/' + cvFile[0].FileType + '\r\n' + 'Content-Transfer-Encoding: base64\r\n' + '\r\n' + bodyEncoded + close_delim;
//,"parents" :[{"id":"'+Folder Id+'"}]}' add folder ID if needed system.debug('==================body======================'+body);
HttpRequest req = new HttpRequest();
req.setheader('Content-Length',String.valueOf(body.length()));
req.setheader('Content-Type','multipart/related; boundary='+boundary);
req.setMethod('POST');
req.setEndpoint('callout:Drive');
req.setBody(body);
Http h = new Http();
try {
Httpresponse resp = h.send(req);
if(resp.getStatusCode() <= 299) {
rw.isSuccess = True;
System.debug( resp.getBody()+'===='+resp.getStatus() );
Map<String,object> responseMap =(Map<String,object>)JSON.deserializeUntyped(resp.getBody()) ;
system.debug('========='+responseMap);
rw.response = 'File Upload Successfully. File Id:'+responseMap.get('id');
}
else{
rw.isSuccess = False;
System.debug( resp.getBody()+'===='+resp.getStatus() );
rw.response = 'Error : '+resp.getBody();
}
}
catch(Exception ex) {
rw.isSuccess = False;
rw.response = 'Error : '+ex.getMessage();
}
return new List<ResponseWrapper>{rw};
//After file was successfully upload we can delete the file if needed
//delete new ContentDocument(Id = cvFile[0].ContentDocumentId);
}
public class ResponseWrapper {
@InvocableVariable(label='Response' description='Response' required=true)
public String response;
@InvocableVariable(label='Success Flag' description='Success Flag' required=true)
public boolean isSuccess;
}
}
So we are using Named credentials so we don’t need to pass access token. We have also pass file metadata with out that in simple request we will lost the file name and other details. We can also pass parent folder Id if needed.
Finally we need to drop this screen component on record detail page. Above all we can use this component with any supported object. For Instance simply Drop the Flow component and select our flow. And for recordId select the checkbox to pass record Id.
Final Output



So this is how our final output will look like:
So we have Upload File to Google Drive Using Flow and Named Credentials. As this is a reusable component so we can use this component with multiple objects. Once user select the file and save it, we will return the File Id which we can store if needed.
So did you like this post or do you have any questions, let me know in comments. Happy Programming 🙂
Hello, does this tool work from within visual flows?
yes it include redirects so you need to modify it to handle them according to flow.
This workds great….one update.To place the file in a folder i used the following:
I added this string.
String FolderId = ‘YOUR_FOLDER_ID’;
And in the string body add “+ ‘”parents” : [“‘ + FolderId +'”],'”. Here it is in context:
String body = delimiter + ‘Content-Type: application/json\r\n\r\n’ + ‘{ “name” : “‘ + cvFile[0].ContentDocument.Title + ‘”,’ + ‘”parents” : [“‘ + FolderId +'”],’ + ‘ “mimeType” : “image/’ + cvFile[0].FileType + ‘”}’ + delimiter + ‘Content-Type: image/’ + cvFile[0].FileType + ‘\r\n’ + ‘Content-Transfer-Encoding: base64\r\n’ + ‘\r\n’ + bodyEncoded + close_delim;
In googleapi V3 they changes the syntax for “parents”…so the parent syntax as written above doesnt work anymore
Can you please tell me how you have created flow for this process
Hey @srathjen ,
Can you please let me know how you have created this complete Flow for this process .
Thank you
I’mgetting this error
|DEBUG|Bad content type. Please use multipart.====Bad Request
HttpRequest req = new HttpRequest();
req.setMethod(‘POST’);
req.setHeader(‘content-type’, ‘application/json’);
req.setEndpoint(‘callout:google’);
String body = ‘{“name” : “Hello1″,”mimeType” : “application/vnd.google-apps.folder”}’;
req.setTimeout(60*1000);
req.setBody(body);
Http h = new Http();
try {
Httpresponse resp = h.send(req);
if(resp.getStatusCode() <= 299) {
System.debug( resp.getBody()+'===='+resp.getStatus() );
Map responseMap =(Map)JSON.deserializeUntyped(resp.getBody()) ;
system.debug(‘=========’+responseMap);
system.debug(‘=========’+responseMap.get(‘id’));
}
else{
System.debug( resp.getBody()+’====’+resp.getStatus() );
}
}
catch(Exception ex) {
system.debug(‘Error’+ex.getMessage());
}
i want to create a folder but it creating a untitle file any reason why?
What endpoint you are using?