Back to blogYouTube Video

Published November 3, 2025

Syncing projects with Google Drive - Part 4 (Handling Subfolders and Duplicates)

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

AI Summary

This video is the fourth part of a series on building a project syncing tool with Google Drive. It focuses on two critical technical implementations: the recursive handling of subfolders to ensure entire directory trees are uploaded, and the logic required to detect and manage duplicate files to prevent redundant uploads.

Key Takeaways

  • Implementation of subfolder support to maintain local directory structures within Google Drive.
  • Development of a mechanism to identify and handle duplicate file uploads.

Description

In this part, we're gonna set up handling subfolders and dealing with duplicate uploads. Github: https://github.com/hassanaziz0012/drive-syncer-video Full Playlist: https://youtube.com/playlist?list=PLTGiYd8gFivjVHK2xB7TLf7XLW8rmKXRM&si=L97UpFIHgfHbvLMT LINKS Website: https://www.hassandev.me My Book: https://www.hassandev.me/designing-websites X / Twitter: https://x.com/nothassanaziz

Transcript

Auto-generated transcript
I'm back. We're going to continue building the DriveSinker project. This is probably part four, I guess. I don't really keep track of those things. I just record, I just build features, record them, shove them to YouTube. Anyway, that was a good intro, right? Anyway, in the previous video, we built a couple of new features. We uploaded, not uploaded, we created this upload folder function, which is going to upload our project folder and all of the files inside it to Google Drive The second thing we did is we created this base folder function over here That's going to create a base folder where we store all of our projects inside of it, right? So pretty good progress for one video in the in this video I want to create one or two new features depending on how much time we have. I just started recording I'm gonna try and keep this to like 15 20 minutes of recording. So we'll see how many features we can build in that timeframe frame. First of all, I want to be able to delete any previous folders. So let me let me show you a problem over here. Basically in my Google Drive we have this folder that I just synced, right? But you will recall that I told you in the previous video that Google Drive does not care about folder names as unique, right? It's just going to duplicate the entire folder if you try to upload the same folder twice and we'll see that in just a second in fact we can already see it over here you can see instead of updating this folder it just created a new folder a duplicate folder with the exact same file contents right we don't want that obviously and there's a there's an easy way to fix this issue and there's a very complex way to fix this issue I'm going to go with the easy way which is to just delete the folder and then re-upload it the complex way is to basically track the file changes for every single file track the I guess the md5 or the shot 256 file checksums and just make sure that there were no changes in the file if there were changes in a specific file like for instance if I if this if this main.go file was unchanged but this sync.go file was changed I added some new code over here then what the what we should do in that case is leave the main.go file unchanged and just leave it on Google Drive. Whereas we would upload this sync.go file because this is the one that changed. That's a much more complicated approach to solving this problem. Why? Because you have to keep track of every single file that changes. You have to keep track of every single checksum for every single file and it just gets more complicated, right? We're not going to do that. What I'm going to do in this project is I'm going to literally just delete the previous folder and then just re-upload it. So to do that let's go down here and create a function. We'll call this delete previous folder. This will take the drive obviously. It'll also take the base folder id which will be a string. You'll see why we need this in just a second and it will take the folder as well which is the folder we're trying to delete and the only thing it can return is an error. So there's an error in trying to delete this previous folder we'll we'll return that now in order to delete the previous folder we want to first find it right and uh quick hint we did something similar up here in the create base folder function right you basically want to create a query like this and you want to use that to find the previous folder so let's create a query over here let's say fmt.sprintf we'll say mime type wrap it in single quotes add a string over here and print the folder mime type over here we'll say that this is a folder and the name of this folder wrapped in single quotes is going to be folder.name and again wrap in single quotes a string over here in parents and here we'll say the base folder id so we're saying that the base folder is a parent of this folder this project folder because every single project that we upload is going to be inside this base folder right so we're basically just checking that over here right and trash equals false right so quick review all I'm saying over here is check the mime type to make sure this is a folder check the folder name to make sure this is the one we're looking for check to make sure it is inside the base folder which is where we store all of our projects and check to make sure that it's not trashed. It's not inside the recycle bin. It is an actual folder, right? That's the query. Now let's run this. Let's say drive.api.files.list.q, pass in the query, .fields. And here we can say files, id and name. And one more function, we can say page size equals one. So basically limit the search results to only one search result because this should be unique right every single project should have a unique folder name right and that's all we need to do let's run do grab the response and an error over here let's handle the error as well if there is an error let's just return it over here now let's say log print f attempting to delete previous folder uh for project and then print the folder name over here with a new line so pass in the folder name that will be folder dot name and then we need to add a couple of checks over here. So first of all, let's say something like if length r.files equals zero. So basically, if we were not able to find this folder, if this folder does not exist, what does that mean? It means that this folder does not exist and we can just upload it new, right? So let's say something like previous folder does not exist, uploading new and just return nil, right? This is what's going to happen if you're uploading a project for the first time, right it's not going to be on google drive by default right if you're uploading a new project it's not going to be on google drive in the first place so this conditional will run you'll return nil and you'll just continue uploading the folder as expected right else if length of r.files equals one which means that this folder does exist already this is where we actually need to delete So I'm going to say log.printf, deleting previous folder, print the folder name with a new line as well, pass the folder name over here and then I not going to return from the function over here so a final check over here else if length r is let say greater than one so basically there duplicate folders with the same name in this folder right there's duplicate folders duplicate project folders inside the base folder with this exact same name right that match this whole query right if that's the case then we should say something like log.printf or not log.printf but like return errors dot new will basically create a new error over here and we'll say multiple folder matches returned from API Cannot delete right because if you find multiple folders with this with this exact same query Which one do you delete right? Which one is the old folder? Which one is the new which one? Are you supposed to delete which one are you supposed to keep you have no idea, right? So we'll just skip that return an error adding it see um there's three conditionals here in this one we return in this one we return but in the second one we were supposed to delete the previous folder we don't return and the reason is because down here i'm going to delete that folder this is going to be pretty simple we'll just say drive.api.files.delete pass in the folder id which will be r.files um zero the first element in there dot id and then run do to actually execute this we'll get a response and stuff or actually we won't get a response we'll just get an error over here not not not a response so let's just say error equals this since error is already declared up here we don't need a colon remove that if there's an error let's return it and if there's no error let's return nil so with all of that done error strings should not end with punctuation or new lines okay let's delete this full stop over here and that should be fine perfect now back up here in the upload folder function let's actually use this so right after we create the base folder we can say something like if error equals delete previous folder why can't i get intellisense to work in these scenarios error does not equal to nil let me just do it this way guys um if delete previous folder pass in the drive now it works pass in the base folder id and pass in the folder as well so all i'm saying is try to delete the previous folder grab the error value that you get from this function right because this because this is going to return an error grab that error value put it in this error variable and if that error is nil or if it's not nil if there is an error in trying to delete that um folder let's say log.print f let's say fail to create fail to delete previous folder you may see duplicates in your upload and then we can print the error over here with a new line so print the error and you might wonder why I'm not using log dot fatal F over here to exit the program and why I'm only using log dot print F and allowing the program to still continue to run well the reason is since Google Drive does allow duplicates I kind of just what I'm going to do is let the upload continue we're not going to crash the program since this is not a critical issue we can still proceed with the upload after this but we do need to inform the user that this is going to occur so that's what the log here is for. Now hopefully I did everything correctly. I'm going to delete everything in my Google Drive. So we start fresh. We're going to first of all just upload everything once more and this should work perfectly fine. As you can see attempting to delete previous folder. Previous folder does not exist. It's basically going to try to delete it and if it doesn't exist already it's just going to upload a new folder which is what it's doing right now. Okay almost finished. there we go and here's the folder and here are all the files perfect this is what we had before as well right so what's changed well if i run the whole thing again you know that previously it was going to create a duplicate folder over here right but this time this time it's going to where's my there it is it's going to attempt to delete the previous folder and you can see over here deleting previous folder drive sync video it deleted the previous folder and it's uploading all of these new files to the new folder over here and you can see all of them exist all of them were uploaded successfully awesome so this functionality works we can now delete the previous folders and upload all of the new files successfully the second thing i want to implement and hopefully the video isn't getting too long okay 14 minutes we still have time we still have plenty of time the second thing that i want to implement is subfolders so you can see over here um where is it for instance this test.txt file you see over here yeah you can see i have these um subfolders over here and inside this subfolder is the test.txt file but on google drive you don't see any subfolder right this test.txt file just got uploaded to the root folder over here right to the root project folder instead of a subfolder so we need to implement the subfolder functionality before I do that I want to clean up this code a little bit I want to say something like let's grab all of this and try to put it inside a different function let's say upload file and there we go so this is going to require a bunch of arguments let's try to add all of them over here path as well I guess which will be a string it's going to return an error let's add that over here as well it's going to also need the d variable which is fs.dir entry the directory over here and the project folder as well so let's put that over here as well this will this will go what am i saying this is going to be a drive.file pointer and that's all we need to do over here right awesome now we can just run this so over here in the filepad.walkdir function you can see if the directory or if the object that we're looking at over here is a directory, then we return nil. But if it's a file, then we should run upload file, we should pass in the arguments, the drive, the path, the dir entry and the project folder over here. And they should still work, right If it doesn work then we grab the error over here and we return the error as such Or you can even simplify this to something like return this and that work as well Let do one quick upload just to make sure that this is still working and that we didn't break something over here. Okay, it's still uploading files. Perfect. So this works. Now we can upload files through this function. But now we also want to upload folders over here, right? Instead of return nil. This is going to be a bit more complicated. it but let's try to work through this let's say create subfolder i'm going to move this functionality to its own sub function over here right this will take the drive it'll take the parent or actually the parent id which will just be a string and it will take the fs.dir entry as well and it can return a google drive file along with an error message right now the subfolder that we create over here which will be drive dot file uh what was that file there we go the name will just be d dot name the mime type again as you know every single google drive folder must have this mime type over here right and one more thing it must also have a parent all right and the parent will be a list of strings and we'll pass in the parent id over here this is how we create a tree of directories right we'll pass in we'll basically create subfolders within subfolders right by using this parent id over here and now we can just say something like response and error equals drive.api.piles.create pass in the sub variable over here as a reference run do this should be very simple because we've we've created multiple folders like this already right so you should have a very good idea of what i'm doing over here make sure to handle the error as well we can say something like return nil and error otherwise let's return r and nil over here and that should be fine and voila now we can create subfolders but we're not done yet we also need to figure out the exact parents for every single one of these subfolders right so let's create the functionality to do that first of all let's create a map over here of subfolders this will be a map of string of string and the first one i'm going to add over here is the base folder as such there we there we go and let's fix this name as well basically this will be a map of subfolders we're going to put all of the subfolders in this project folder in this map the key will be the folder name and the id over here the value will be the folder id on google drive right so here's where this is useful we're basically going to store all of the subfolders that we create inside of this map and then every time we want to upload a file within that subfolder we'll just give the folder the subfolder name over here and we'll get the subfolder id and then we can use that to put that file inside that parent folder in google drive right that is why we use a map over here if this does if this doesn't make sense right now it will once we implement this now inside this um d dot is there we're going to say sub name this will be the subfolder name and we'll use file path dot base and pass in the path over here this will return the last element of the path all right this will be the folder name the name of the subfolder right so let me let me walk you through this you can see that this check over here is going to make sure that this is a directory right so if i have a path like something folder slash subfolder slash subfolder right filepad.base will give me the where's the hover why can't i hover over this okay anyway yeah there we go filepad.base will return the last element of the path right in this case the last element of the path is this subfolder right here right this subfolder so this will return the subfolder name and then we need the parent name all right so which parent folder is um the parents of this subfolder right we want to grab that as well so we can place this subfolder inside the appropriate directory right for that what we're going to do is first of all run filepad.dir this will return all but the last element of the path and then we'll pass in the path over here and then we'll say filepad dot base on this as well this will give us the parent name so again let me show you an example over here inside of a comment let me actually zoom in as well i don't know if you guys can see the code as well as i can but let me zoom in um if i have a path like let's say project slash folder slash subfolder right slash um never mind let's just keep it this simple so in a path like this the sub name is going to be the sub folder over here and the parent name is going to be folder over here right we want to make sure that we upload this subfolder inside of this folder over here right this parent we don't want to like if this is the local um path on our machine we don't want to have something like project slash subfolder on our google drive right we want to make sure that we upload it to the appropriate folder like this right that's what i'm trying to do over here so let's zoom out again the sub name is the name of the subfolder the parent name is the name of the parent folder that houses this subfolder right and we need both if we want to upload this properly now we can just run a conditional like if okay subfolders now we can use this map pass in the sub name and run if not okay if this doesn't exist over there then we can say something like parent id and p ok which is parent ok and run the subfolders again and pass in the parent name so basically if this subfolder doesn't exist already then we need to create it right so let's um we'll create it inside of this conditional inside of this block of code over here right and And before we create that subfolder, we want to grab the ID of the parent, right? Which we will do from this subfolder's map. All right, so let's say var parent string. This will be the actual ID. Let's say that if it exists, right? If the parent ID exists, that's what this POK variable is going to tell us. Then we'll say parent equals the parent ID, right? Otherwise, if this parent doesn't exist, right? If this parent doesn't exist, then the parent will just be the base folder and its ID, Right and now we can finally create that folder over here by running the create sub folder function passing the drive pass in the parent pass in d which is the directory entry over here This will return two things. It will return the subfolder and an error over here. Make sure to handle the error as well. Let's just say return error and let's say log.println. Fail to create subfolder. Instead of println let's use printf i like using printf there we go print the subfolder name over here and print the error after that add a new line sub dot um where is it sub name and error there we go now assuming this worked let's add this to the map of subfolders we have up here right we'll say subfolder sub name because the key is the name of the folder and the value in this map is going to be the id of that folder on google drive right and then we can add a bunch of logs again over here we can say something like log.printf created subfolder print the subfolder name over here in parent we can print the parent as well and i think that's all we need to do right now we want to make sure that we upload every single file to the appropriate subfolder as well so this will take a bit more code and by the way before i do that let me explain all of this as well real quick we first of all make sure that we or actually we first of all make sure that we are inside of a directory right if we are inside of the directory then this sub name will be the name of the sub folder that we're trying to create and this parent name will be the parent folder of that sub folder right so we make sure that we up we create the sub folders inside of the appropriate folders right everything else is pretty straightforward if it already exists then we do nothing but if it doesn't exist if this subfolder doesn't exist then we create the subfolder over here right and we make sure to upload it to the appropriate parent folder by using this block of code over here right and then finally we just make sure to add that new subfolder to the subfolder map so we can use it in uploading new files and folders to that subfolder right super simple now in this else block where we're uploading files let's say parent name equals file path dot base file path dot dir and then pass in the path over here and then let's say if parent id and okay equals subfolders pass in the parent name over here and then make sure that we check for okay over here if this works then we should just say return upload file over here else or by the way we should instead of using project folder over here for the parent we should probably pass in parent id and that'll be wait a second this doesn't accept parents okay we have a problem over here um in this upload file function instead of using the project folder this should be renamed to parent id and it should take a string not a drive.file so we can use that over here as such there we go that's all we need to do and then just make sure to pass that in over here right else if we can't find the parent for this folder for this file let's just um return an error um and before we return an error let's print the error as well let's say log.printf error and the error over here will just be um i don't know fail to find parent folder print the folder name over here for file print a file over here and just say upload failed right pass that in over here and in errors dot new as well and instead of this empty string let's use FMT dot s print F so I can actually where did I go where did I go what the hell what what just happened okay there I am I accidentally pressed page down and lost my footing okay print the parent name over here print the file file name as well which will be d.name and that's all we need to do now why is this giving me an error it's missing a return okay we need to return we need to return where do we need to return over here right let's just say return nil over here okay whole thing works this project folder isn't really necessary anymore you'll see why because now that we we can comment this whole thing out now that we are creating all of these subfolders this filepad.walkdirt will also create the project folder for us you'll see how that works let me make sure that my drive is empty or actually we don't even need to delete this previous project because this deal where is it this delete previous folder will do that for us so let's just run this whole thing and see if it works it's deleting the previous folder it's creating the subfolder in parent programming you can see this is the project folder and it's going to upload all of these files and And oh my god, this video is getting too long as well. Oh my god, oh my god, oh my god. What am I going to do about this? See, I create such complex projects that I just have to make long videos. And then I don't like making long videos because they don't get as much engagement as shorter videos. Anyway, this is the project that we uploaded. You can see over here, now we have these subfolders. There's no test.txt file over here. It's going to be inside of this subfolder. There it is. It has the text over here. So it was uploaded successfully and we also have this sub 2 which will have the subtext file With its content it was uploaded successfully Just like I have over here in my path, right? so now we can upload subfolders as well and we can also delete the previous folder and With that the video is getting way too long. So we're going to stop there and Yeah, that's the end of part 4 boys In the next video, I'll probably implement the gitignore stuff over here so that we can ignore all of these files inside of our gitignore as well as the files in config.json that we have in this ignore list so we don't need to upload all of those files, right? We'll implement that in the next part of the video. That's a pretty simple feature to implement. And yeah, that's the end of part four. Like, comment, subscribe, all of those things and see you in the next video. Thank you for watching and yeah, I'm not really good with outros, with video outros. So I'm just gonna say bye-bye, have a nice day and yeah, that's all.

Share this article

All great things started with a conversation

If you've got a cool project or opportunity and you want me to be a part of it, set up a free meeting with me here, and let's talk. 😊