This Tutorial was completely rewritten in April, 2021
Uploading files to the Google Drive API V3 can be a little confusing in the beginner. I have seen a lot of questions and issues related to it on Stack Overflow over the years. These issues normally stem from the fact that creating a file is actually done in two parts, the first being creating the metadata that being the name, description
and mimeType. Once the file is created with its meta data only then should we upload the file in the form of a file stream. If you fail to create the file metadata then you will upload a file that is unnamed.
The second issue comes when you try to update a file that already exists on Google Drive, most people just try to use the same method that being file.create. Then they are confused when a second file rather than updating and overwriting the existing file which is what you actually want to do.
In this case you should be using the file update method and not the file create method. Then again the confusion comes in with the fact that its two part
you can update the file metadata, and then update the file itself by uploading a file stream. The final issue with file.update comes in with the fact that
update is in the form of HTTP patch methodology and not all of the properties of a file object are actually writeable.
So you may end up getting an error stating that the field you are trying to update is not actually writeable.
Setup
I thought this time we would look into using a service account. Think of a service account as a dummy user. You can pre authorize a service account granting it access to your data. So i can take the service accounts email address and share with it a directory on my Google Drive account then it will have access
to upload to that directory on my google drive account. It’s that simple there will be no need for user interaction and a consent screen to popup.
You should consider using a service account if you are going to be accessing private data that you the developer own, if you are going to be accessing
data that belongs to another user then you should be using Oauth2.
So you will need to go to Google developer console and create a new service account. Download the json key file and don’t forget to enable the google drive api
under libraires. If you have any issues I have a video which you can go check out and it will walk you through creating
service account credential key file.
How to create Google Oauth2 Service account credentials.
Create .net core console application
For this i am going to create a .net core console application, but you can use service accounts, with libraries or with asp .net core its up to you which type
of project you create but for this example we are going to keep it simple and just use a console app.
you will need to include the google.apis.drive.v3 nuget package.
Add constant
At the top of my program class i am going to create a couple of constants.
The first is the path to the credentials.json file we downloaded from google developer console, as always don’t share your credentials with anyone these should
be stored save and secure for only your team to access.
I will need another constant for the service account email address, you can get this from google developer console or you can just open the key file in a text editor
and you will find it there its the one that contains an @ and looks kind of like an email address.
In my project i have created a simple text document called Test hello.txt remember to set this file to copy always under its properties. So i have added the name
as a constant here just for consistency.
Finally i have a directory id constant which is the file id of the directory on my google drive account which i shared with the service account. The easiest way
to find the directory name here is to do a file.list and it will list all of the files that the service account has access to it will also return the directory
that we shared with it so we can get the file id we will need to upload our files.
private const string PathToServiceAccountKeyFile = @"C:\Youtube\dev\ServiceAccountCred.json"; private const string ServiceAccountEmail = "driveuploadtest@testapikey-305109.iam.gserviceaccount.com"; private const string UploadFileName = "Test hello.txt"; private const string DirectoryId = "10krlloIS2i_2u_ewkdv3_1NqcpmWSL1w";
Authorize service account
Ok now we we will need to load the credentials into our application. We simply load the credentials key file and set which scope we will be using. Due to the fact that we will
be uploading a file we will need write access to the drive account. so we will be using the drive scope, to attain full access to the users drive account.
// Load the Service account credentials and define the scope of its access. var credential = GoogleCredential.FromFile(PathToServiceAccountKeyFile) .CreateScoped(DriveService.ScopeConstants.Drive);
create drive service
Finally we will create our drive service. All calls to the Google drive api will be run though the drive service object.
// Create the Drive service. var service = new DriveService(new BaseClientService.Initializer() { HttpClientInitializer = credential });
Create a new file on Google Drive and upload it
Remember I mentioned the metadata of the file. If you dont set the files metadata then the file will upload with a name of unnamed. So for the very minimum we
should set the name of the file. i am also going to set parents. If i don’t set parents the file will be uploaded to the root folder of the service account.
// Upload file Metadata var fileMetadata = new Google.Apis.Drive.v3.Data.File() { Name = "Test hello uploaded.txt", Parents = new List() {"10krlloIS2i_2u_ewkdv3_1NqcpmWSL1w"} };
Remember i mentioned that a service account is a dummy user well this dummy user actually has its own google drive account and you can upload files to it.
By setting parents we well be uploading the file to the directory i shared with the service account.
Now in order to upload the file itself we will need to load it into a filestream. Once we have done that we can call the service.file.create method
and pass it the file metadata, and the filestream itself. Finally we will call upload async to upload the file to google drive.
string uploadedFileId; // Create a new file on Google Drive await using (var fsSource = new FileStream(UploadFileName, FileMode.Open, FileAccess.Read)) { // Create a new file, with metadata and stream. var request = service.Files.Create(fileMetadata, fsSource, "text/plain"); request.Fields = "*"; var results = await request.UploadAsync(CancellationToken.None); if (results.Status == UploadStatus.Failed) { Console.WriteLine($"Error uploading file: {results.Exception.Message}"); } // the file id of the new file we created uploadedFileId = request.ResponseBody?.Id; }
The response body on our request will contain the id of the newly created file.
If we check google drive sure enough we have a file there now. Remember if you didn’t add parents that the file will be uploaded to the service accounts google drive account and you won’t be able to see it unless you do a files.list
Updating an existing file on Google Drive
Lets try to update the file we just created.
Remember i mentioned that we still need metadata. I create a new file object and i can add a name to it. If you don’t actually want to update the metadata you could
just pass the same name and all would work. If you do actually want to change something in the metadata it is here you would do that. Just remember that
not all of the fields are writable Id for example is not writeable.
// Let's change the files name. // Note: not all fields are writeable watch out, you cant just send uploadedFile back. var updateFileBody = new Google.Apis.Drive.v3.Data.File() { Name = "update.txt" };
I thought I would make a quick change to the file itself by adding some text so that we can see that it is in fact uploaded.
// Let's add some text to our file. await File.WriteAllTextAsync(UploadFileName, "Text changed in file.");
Then we again load the file stream and this time instead of calling files.create we will call files.update and pass it the updated file metadata, the file id of
the file we are trying to update and the stream. and again call updateasync
// Then upload the file again with a new name and new data. await using (var uploadStream = new FileStream(UploadFileName, FileMode.Open, FileAccess.Read)) { // Update the file id, with new metadata and stream. var updateRequest = service.Files.Update(updateFileBody, uploadedFile.Id, uploadStream, "text/plain"); var result = await updateRequest.UploadAsync(CancellationToken.None); if (result.Status == UploadStatus.Failed) { Console.WriteLine($"Error uploading file: {result.Exception.Message}"); } }
Now when we check Google Drive again we can see sure enough our file name is updated and the contents have been updated as well.
Conclusion
Uploading files with Google Drive api can be a little confusing you just need to remember that if you are uploading to an existing file you will need to use
update and if you are creating a new file you will need to use update. Also remember when updating that not all fields in the file metadata are writable.
I’m confused about something concerning Google Drive Api v3. This is a scenario I use now :
WinForm application project, VS2015
Two Forms
[Form1 to authenticate user to use my Google Drive Folder(ThisFolder)]
[Form2 to upload files to my google drive folder(ThisFolder)]
[Form1]
I use a native code to receive access Token [using loopback url] and save it to User settings file (My.Settings.accesstoken=received_access_token)
[Form2]
When I use your Example, it still opens web browser to authenticate user before uploading.
and uploading is done.
My problem is that I don’t want to re-authenticate the user each time the user tries to upload a file.
If your allowing them to upload to your drive account then you should be using a service account. Which wouldn’t request authorization.
Here is an example as well.
Hi Linda,
This is a great tutorial. Would the process be the same if I wanted to load a file to YouTube (using the YouTube scope)?
No because the YouTube api does not support service accounts. If you want to upload videos to YouTube you need to use Oauth2. If you store a refresh token then you can use the refresh token to request a new access token when ever need.
Just remember that you will need to have our project set to production or your refresh token will only work for seven days, also your app will need to be verified or your videos will only be uploaded as private.
Great Tutorial !!!!
one small comment (struggled with it for a while): in order to get the directory Id , you have to go to the driver and copy the end of the url
so if your google drive (when you are in the shared folder) url looks like this :
https://drive.google.com/drive/u/4/folders/XXXXXXXX -> so your directoryId would be : XXXXXXXX (example of course.. 🙂 )
hi;
where do we find this number ” DirectoryId = “10krlloIS2i_2u_ewkdv3_1NqcpmWSL1w” ?
This is the id of the directory you want to upload to.
The easiest way to find it is to do a file.list and search for the name of the folder you are looking for. It can also be found in the google drive web application in the url bar.
I posted on youtube and got a reply from you, but for some reason I’m unable to post a reply, so I figured I’d take my chances here=)
Thanks for all your great content – it’s been super helpful!
I’ve successfully shared a folder on my drive with the service account and it’s working great, but when I share it with a folder in our company drive, it doesn’t. Any ideas?
– The service account is set as a Manager on the workspace account drive (doesn’t work)
– and as an Editor on my personal company drive (works)
The workspace account drive does not have a choice to select editor directly – but managers have full control over the drive.
I’m currently going down this path: [https://developers.google.com/cloud-search/docs/guides/delegation]() to give the “https://www.googleapis.com/auth/drive” scope to the service account.
It feels like this would, if it worked, give a bit too broad permissions – like access to everyone’s personal drives, which would not be ideal.
Thoughts?
To use a servie account with google workspace the admin of the workspace account needs to configure domain wide dedication and your code needs to impersonate a user on the domain that has access.
Thanks again!=)
That looked promising – especially “your code needs to impersonate a user…”
The link you provided only had a Java example – and I’m having some trouble finding the equivalent for .NET
– We enabled Domain wide delegation for with the https://www.googleapis.com/auth/drive scope
– I tried the .Impersonate() function on the GoogleCredential object, first time I got a promising note to enable IAM Service Account Credentials API for my project, but after enabling that I got an error:
“error”: {
“code”: 403,
“message”: “The caller does not have permission”,
“errors”: [
{
“message”: “The caller does not have permission”,
“domain”: “global”,
“reason”: “forbidden”
}
],
“status”: “PERMISSION_DENIED”
}
}
“, Description:””, Uri:””.
This is the code I used to create the credential
var credential = GoogleCredential.FromJson(googleServiceAccountCreds)
.CreateScoped(DriveService.ScopeConstants.Drive)
//This one loads, but throws “The called does not have permission”
.Impersonate(new ImpersonatedCredential.Initializer(“app-dev@project.iam.gserviceaccount.com”));
I found this one of yours though: https://github.com/LindaLawton/Google-Dotnet-Samples/blob/master/Samples/Drive%20API/v3/ServiceAccount.cs and it feels like I’m on the right track.
From my understanding the json creds have the service account email (which is permissioned to the drive)
When I try it without the .Impersonate, I get what I got initially: File not found: [file id] – but it still works just fine when I point it to my personal workspace drive.
The caller does not have permission most likely means that the user you are authenticating with does not have access to the data. Either you didn’t configure delegation properly or you didn’t add the delegation user in your code.
Hey Linda,
first of all thank you for your great effort.
Would you might help me with troubleshooting. For the loading of the account credentials, I do get the following exception:
“System.InvalidOperationException: “Error creating credential from JSON or JSON parameters. Unrecognized credential type .”
Looking forward to hearing from you.
Kind regards!
Put up a question on stack overflow with your code if you want I will have a look. Tag it:
how to get link upload file on google drive using c#
That depends on the file type. Check webContentLink from a file.get response. If its null then your file needs to be exported first so you wont get a direct download link via the api. Your going to have to download the file yourself using file.export and covert it from a google docs mimetype to a diffrent mimetype
Great content. Eased a lot of pain for my because I was wandering aimlesly on the internet trying to find a comprehensive step by step guide on how to get a functioning application able to interact with Google Drive. And here I found just that, plus a lot more insights on how it all works. Thank you!
Thank you very much for your help 🙂
I was able to use the Google Drive API successfully to upload files but I noticed something strange,
I tried to upload a file in the same way to a folder in Share Drive and received an error that the ID of the folder was not successfully found. (The service account was shared in the folder)
Do you have any idea why it would work on a personal Drive account and not on a Share Drive??
In terms of the Google Drive API, is there any difference between a personal Drive account and a Share Drive account??
Thanks Itamar 🙂