← Back to blogYouTube Video

Published October 31, 2025

Syncing projects with Google Drive - Part 3 (Upload to Drive)

Part 3 of 19 in Syncing Projects with Google Drive

Can't play the video or having issues? Here's the direct link.

Summary

I implement the upload half of my Drive Syncer by creating Google Drive folders and pushing every local file inside a configured project folder up to Drive. I create the base folder first, query Drive by mimeType, name and trashed=false so I don’t create duplicate folders, then create each project folder inside it using the application/vnd.google-apps.folder MIME type. From there I walk the local folder with filepath.Walk, skip directories, open each file, and upload it with files.create plus Media, setting the project folder ID as the parent. I use log.Fatal when base or project folder creation fails because there is no point continuing without a destination. I loop over the folders in config.json, and I verify in Google Drive that the project folder and all files uploaded correctly.

Key takeaways

  • Use application/vnd.google-apps.folder as the MIME type when creating a Google Drive folder; files and folders are distinguished by ID, not name.
  • Query for an existing base folder before creating it using mimeType, name and trashed=false, and treat multiple matches as an error so uploads never go to an ambiguous destination.
  • Walk the local project with filepath.Walk and skip directories with d.IsDir() so only files get uploaded.
  • Set the destination folder ID in the file metadata’s parents field to place each uploaded file inside the correct project folder.
  • Stream uploads with files.create().Media(io.Reader) using the opened os.File, and always defer file.Close() while handling both open and upload errors.
  • Use log.Fatal when base or project folder creation fails, because continuing without a destination folder is pointless.
  • Loop over the folders list in config.json to upload multiple projects into one organized base folder.

Description

Gonna write the functionality to upload to the actual GDrive servers. Github: https://github.com/hassanaziz0012/drive-syncer-video

Transcript

Welcome back, ladies and gentlemen, to part 3 of the DriveSinker project. Now, I am recording this for the second time because the last time I recorded this, my video file got corrupted. And so I have to record this a second time. You may see some new stuff over here, like this subfolder over here. I don't think I had this at the end of part 2. But you can just ignore that for now because we are going to work with this in this part of the video. And yeah, let me just quickly recap what we did. We built this config struct over here, including a config file that looks something like this. And we also built this drive struct over here, this new drive function, the connect function, and all of this is just going to give us the Google Drive service, right? The API that we can use to interact with our Google Drive to upload files and folders and so on and so forth, right? Yeah, so in this video, we're going to finally implement the functionality to upload files and folders to Google Drive. I am going to do this in a new file, and I'll call this sync.go because it's synchronizing between Google Drive and our local machine, right? Not the most intelligent of naming sense, but it'll do for us. So let's create a function over here called uploadFolder. This will upload the entire folder that we give it to Google Drive. It will take the drive service, which will be the drive struct that we created earlier, and it'll take a folder, which will be a pointer to the folder object that we have defined in the config over here. Now, let's start implementing this, and this will not return anything for now. I don't need it to return anything. First of all, let's just add some logs over here. Let's just say, "Starting upload process for folder," and print the folder name over here and add a new line as well. There we go. And now we can actually create this folder. So, to create the folder, I'm going to create a variable called metadata, and I'll create the file metadata over here. We'll give it the name of folder.name, and we'll give it the MIME type. This is important: of application/vnd.google-apps.folder. So this is the exact MIME type that you need to pass in when you're trying to create a folder in Google Drive, right? Not a file, a folder. Once you have this, you can just say something like the drive API.files.create and pass in the metadata over here as a reference, and then just run Do over here. And this will create the folder for us. It will return a file and an error. I can just call this r as the folder. All right, or not the folder because the argument over here is named folder as well. Let's call this the, I don't know. Let's just call this projectFolder, all right? And of course, handle the error as well. Let's just say log.Fatalf("Failed to create project folder"). Print the folder name over here and print the error over here. And add a new line as well. Pass in the arguments, the folder name and the error. There we go. And I'm using Fatalf because this will just exit the program, right? If this error occurs, because if we can't create the project folder, then there's no point in continuing and trying to upload files, right? Because there's no folder to upload them to. So, with that done, we can then start to upload all of the files in this project folder, right? So for that, we can use the filepath package, and we can use the Walk function over here. This is going to take a path, which will just be folder.path for us, and then it's gonna run this function that we give it, and it's going to run this function for every single file inside of this folder, right? So we can use this to actually upload every single file. So let's do this now. First of all, we want to create the file metadata, obviously. So let's just call it f over here, or let's actually just call it metadata again. Who cares, right? And let's create drive.File. The name will be from d.Name. Why is this package not imported? There we go, d.Name. And then we want to pass in a parent as well. This is going to be a list of strings. You might be wondering why we are passing a list of strings as the parent for this file instead of just one parent. Like, why doesn't every file only have one parent or one folder above it, right? This is kind of something that's unique to Google Drive. And basically, you can have multiple parents that hold the same file. Basically, a file can have multiple parents, multiple parent folders, and you can kind of access the same file from all of those different folders. All right. So this is something that's unique to Google Drive. We don't really need to worry about it. We're just going to pass in one parent, and that will be the project folder over here. And we'll pass in the ID of that folder over here. So this will put this file inside of the project folder that we define up there. All right. And then what we can do is we can open up the file by saying f and error equals os.Open and then pass in the file path over here. Make sure to handle the error as well. Let's say log.Println, or not log.Println, but let's return the error over here because this function does expect to receive an error over here, right? So let's do that, and make sure to close the file when we're done by using defer. And then you can actually create this file. So let's say drive.API.Files.Create. We're going to pass in the metadata as a reference. Don't forget that. Then we'll run the Media function, and here we'll pass in the file that we want to upload. This accepts any io.Reader object, and luckily for us, every single file is going to implement that interface as well. So we can just pass in the file over here. Run Do. This will return the file and the error, so let's handle both. If there's an error, let's just return it over here. And actually, now that I think about it, let's log all of the errors as well, just so we know what's going on over here, right? Let's say, "Failed to open file," and there we go. Or actually, I wanna, I wanna, I want to have the file name over here as well. So let's just do Printf. It's just so much easier to use Printf. Print a string over here and the error over here, and obviously add a new line as well, and then pass in the file name as well over here. There we go. Let's do something similar down here and say, "Failed to upload file," and then just print the file name and the error that we get from Google Drive. All right, awesome. Now, that should be all we need to do. Let's run a final, or print a final log over here. Let's say, "Uploaded the file name to Drive," and then let's also print the ID of that file over here. So let's pass in, and by the way, make sure to add a new line. Don't ever forget that. Print the file name over here. That'll be d.Name, and the ID of the file on Drive, that'll be this r variable.ID. And that's all we need to do. And we can return nil at the end because this function expects to receive an error over here, right? So, just going to return nil if nothing breaks, right? If there's no error, that should be all we need to do. So let's go back to main.go and up here and just see if this works, right? So let's think about how we want to upload all of our folders. So we can go over to config, and you can see that the config holds a list of folders, and each of these folders we want to synchronize or upload to Google Drive. So we can actually loop over this. We can say something like `for _ in range`. Where's the config? `conf.Folders`. And we can say something like `uploadFolder`, pass in the drive and pass in the folder as well. And as you can see, we only have one folder to upload, which is this current project folder that I'm in. So let's just see if this works, right? And it will probably first make me refresh the authorization token because I haven't opened this project in a while. But let's see, let's see what happens. Okay, so looks like my authorization token already works, but apparently you can't synchronize a folder. So let me go back over here. Basically, what's going on is, what's going on is it's trying to upload this whole folder first, and we can't do that. So let's, what we should do over here is let's say if `d.IsDir` then we can just say `return nil` over here. So we're skipping uploading directories for now. We just want to upload files. All right, so let's clear this. Let's run the whole thing again and see what happens. Okay, so it's starting to upload all of the files over here, and you can see the drive ID over here as well. I'm gonna let this whole thing run, and then I'll open up Google Drive and see if all of this actually worked or not. Okay, so it's done. Let's open up Google Drive. Now here I am in the Google Drive, and you can see that it uploaded this DriveSinker video folder with all of the files. You can see all of them over here. That was successfully uploaded. Yeah, everything works perfect. All of the files got uploaded successfully. Awesome. Now, do we still have time? Yes, we still have time. I try to limit all of my videos to like 15–20 minutes in total, so they're not too long to watch for you guys. Anyway, let's see, let's see what can we do now about this. Actually, let's create the base folder as well. So let me, let me walk you through what I mean over here. So in the config, we have this base folder, right? This is a string. This is basically a folder name, and the purpose of the base folder is that I want to put all of the projects that I synchronize with this application in inside a single folder, right? I don't want them scattered across my Google Drive everywhere, and I have to look for them and all that. I want all of them inside one single base folder, organized neatly, so that I can actually, you know, find them easily. So let's configure this. Let's implement this. This is going to be pretty simple, but there are a couple of new concepts we need to learn. Let create a function over here, `createBaseFolder(drive)`. This will take the drive struct, and it will return a variable called base, which will be `drive.File`. This will be the base folder that we create, right? Now, let implement this. First of all, I'm going to create a variable called `newBase` over here. You'll see why I named this `newBase` in just a second. This will be a `drive.File`. It'll take a name, which will be in `drive.config.baseFolder`. That's the name of this folder that we want to have. It'll take the MIME type, this right here: `application/vnd.google-apps.folder`. This is the MIME type of every single folder on Google Drive. If you want to create a folder on Google Drive, this is the MIME type that you pass in, and it will create that folder for you. Then let's just do something like `r` for response, and error is equal to `drive.api.files.files.create`. There we go. Pass in the `newBase` over here as a reference. Run `do`, and then let's handle the error. Let's say, `log.FatalF("Failed to create base folder: %s\n", drive.config.baseFolder, error)`. Print the base folder over here. Print the error. Add a new line. The base folder will be in `drive.config.baseFolder`. There we go, and then print the error as well. There we go. And again, I'm using `FatalF` to crash the program if we, if we can't create the base folder, right? If there's an error in trying to create the base folder, why am I doing this? Because if I can't even create the base folder, then what purpose is it to try to upload the folders, right? Because all of these folders, all of these projects, I want to upload inside the base folder, right? If I can't even create that, then there's no reason to keep the program running, right? Which is why I use `FatalF` to just exit the program if we get an error over here. Anyway, then we can just say, say something like `base = r`. This is the base variable up here, and we can just return base over here. In fact, we can even skip that part and just do `return r`. It's up to you, honestly. Now, the reason why I named this `newBase` is because there's, there's two parts to this function, to this `createBaseFolder` function, right? Over here, the first part is we want to create the base folder. Okay, the second part is that we want to also first see whether that folder already exists or not, right? Because if it already exists, this is going to create a duplicate folder. Google Drive does not discriminate between folders by their names. It does it by their ID, right? So every single file and folder on Google Drive has an ID, something like all of these weird strings you see over here, right? Every single file and folder on Google Drive is going to have a unique ID like this, which means that folders can have the same name, and it will just create duplicate folders, right? We don't want that. So what we're going to do is we're going to first see if the folder already exists, all right? And we can do that by running a query. So let's create a query over here. This will be a string. Let's do `fmt.Sprintf` because I'm going to need a bunch of variables over here. We're going to say `mimeType =`, and by the way, since we're, since I have to type this whole MIME type out so many times, what I should do is I should extract it into some sort of a constant over here. So I believe you can just do something like `const folderMimeType` and just create something like this, right? And then we can just pass that in over here and over here as well. There we go. And let me just make sure that this doesn't exist anywhere else by searching over here. And you can see this is the only place that this string exists. Okay, perfect. Now let's go back over here. In the query, we want to make sure that this has a MIME type of `%s` over here, `folderMimeType`. There we go. And make sure this is wrapped in single quotes, as you can see over here. And `name =`, again wrapped in single quotes, and `%s` over here. This will be the `drive.config.baseFolder`, basically the folder name. Okay, that's all. And `trashed = false`. So basically, it's not in the trash folder. It hasn't been deleted, basically. All I'm saying over here, this is a search query, basically, is the MIME type must be the folder MIME type, which means this must be a folder. This cannot be a file named programming, right? Programming is the base folder name over here that I'm using, right? Obviously, you can use whatever you want, but the MIME type must be a folder. This must be a folder that we're searching for, not a file, and the name of this folder must match the config-based config.baseFolder that we have over here, and it must not be in the trash or in the recycle bin, right? That's what I'm saying over here. Now we can run this query and search for the folder. We can say something like drive.api.files.list.q. Q is what you use to pass in a query, and then you can also use fields over here. Fields will basically allow us to limit the number of attributes we get back, right? We can say something like files ID and name because that's all we really need to search for right now, right? And then we can just run Do to run the entire function. Then this will return a response and an error. Make sure to handle the error over here. Let's say, "Fail to..." I forgot to actually run the function. log.PrintF("Fail to query base folder. Cannot proceed with synchronization."). Let's just say that. Print the error over here, and it should be... It should be a log.FatalF, not PrintF. And then we can also run something like... We can add a check over here, a check like if r.files, if length of r.files is greater than one, then that means there are multiple folders that match this query, right? We don't want that. We want only one base folder, because if you have multiple base folders, which one are you going to upload all of the projects, projects to, right? So let's just say something like, "Found multiple base folder matches," right? And if there's multiple folders like this, which one do we upload to, right? So ideally, this will only have one match, right? And we can get that one match by just looping over it or directly accessing it like this. I prefer to loop over it, so you can say something like for _ f range fr.files. We can add another check over here. This is not really necessary, but I like to read borrow, so yeah, make sure that the names match, and then if they do, just return f over here, and that's all we need to do. Now this is giving me an error over here because these variables are already defined, so remove this colon and it should work. And it's still giving me an error: cannot use this as drive file, this value in assignment. You know what? Let's just rename this whole thing. Let's just say f over here instead of r, and return f over here. There we go. And I guess we can add the colon back, and that fixes the problem. Awesome! So this function is going to create the base folder for us. Now we can go down here in the upload folder function, and before we do anything else, we should create the base folder. So we'll run this function. We'll pass in the drive. This will return a variable, a base, which will be a drive.File, and then we can pass that in over here where we create the project folder, right? So we can say parents, and the parents of this project folder should be the base folder, right? Base folder ID. There we go. And that's all we need to do over here. Now, first of all, let me just delete everything on my Google Drive, right? And now let's run everything again. Let's clear the screen. Let's run and see what happens. It started the upload process for this folder, and it started uploading all of the files. Let's wait for this to finish first, and then we'll see what happens. Okay, here we are in my Google Drive. You can see it created this programming folder, and it created this drive syncer video folder inside of it. And here are all of the files that we had, right? I can open one of it. Which one do I open? main.go, and you can see all of the code over here. So it uploaded successfully, right? config.go, and so on and so forth, right? All of the code is over here. So this works, and this whole folder was placed inside of this base folder programming, where I want to store all of my projects, which means now I can just go over to config.json, and in this folders list over here, I can just add as many folders as I want. If I have 20 side projects, I can just add all of them over here, and it'll just upload all of them inside this programming folder, which is kind of awesome when you think about it, right? Everything's going to be neatly organized for us. And because we're building a custom application over here, we can basically do whatever we want with this, right? We can add, we can add all sorts of features over here to make our life easier. Anyway, with that, this video just got way too long all of a sudden, so we're going to stop over here. After editing all of this, this will probably end up being a 25, 26-minute video, which is fairly long, but I think you guys can keep up with it, right? Hopefully, you don't have the attention span of a goldfish. Hopefully, you don't have TikTok brains where you're just looking for the next video, right? Hopefully, you can take a 27-minute video. So, we built the basic functionality to upload files and folders over here. In the next video, we're probably going to expand on this and build a couple more features. Stuff like reading the .gitignore files so that we don't upload all of the sensitive files that we want to store locally. We don't upload them to Google Drive, and other stuff as well. We're going to add a bunch more features to this. I've already got all of them planned, and I'm gonna keep building, keep recording videos, and keep building more features. So we'll see how that goes. And again, if you have any questions about any of this code, if you don't understand some part of it, if you have any questions regarding all of that, you should leave those in the comments below. A couple of you guys do ask questions, and I make sure to answer all of them, but most of you don't, which is kind of weird. Like, if you have any questions, you should ask in the comments below so that I can help you out and I can teach you whatever is missing, right? Like, you guys just gotta talk more. You know, you guys, you need to leave more comments. You need to start conversations. You need to talk more, you know? Like, you see a cute girl, and instead of you going up to talk to her, you just stare at her from far away. That's what you guys do when you don't leave comments. So make sure you leave a comment, right? Make sure you go talk to the girl. I mean, make sure you leave a comment and ask any questions if you have any, or just tell me I'm doing a great job because I think I am, but who knows, right? Anyway, I'm just rambling at this point. Let's, let's end the video. It's already too long. Thank you for watching. Thank you so much for following along with this series. Like, comment, subscribe, do all of those wonderful things, and I will see you in the next video, which will be part four of this. Bye.

Share this article

Hassan Aziz pointing up

Dude, you’re at the bottom of my landing page.

Just book a call already if you’re that interested.

You scrolled all the way here.