Google drive API file upload script

If you want to upload file to Google Drive, you will naturally gravitate towards the Google Drive API. While reading about the APIs that Google publishes for almost anything, you will learn about their SDK. The SDK provides you with easy functions for interfacing with the API for various programming languages.

The problem is that the more I used the SDK the more confused I was. The documentation is often unclear, not all bindings are implemented across all languages. But above all, the thing that made me dislike the SDK is the fact that uploading a file to Google Drive took 5 times the amount of memory than the file itself. Meaning that the datastructure they use and how they pass it around is SUPER LAME. Not a huge deal if you’re uploading a few doc files but definitely crappy for GB range files. It’s just not right and completes the pattern of “meh” surrounding the SDK.

What is clear & consistently documented is every single API call you can make. It was time to go straight to the API. Sure there is still quite a bit of poking involved in getting something working exactly right but my experience with the API has been a lot better. It has also helped me understand the mindset & design so figuring out new things is much faster.


HTTP, every API call uses it. You can use any technology that you are familiar with to do your HTTP call but I have a penchant for PHP + cURL.

The script

It will read a file in chunks and upload them consecutively. Do to so it uses the API’s resumable uploads. As such it will never consume more RAM that the configurable chunk size.


It still needs a few improvements at the moment but it’s functional.

What it solves

  • authentication: getting a new access token from a refresh token as needed
  • curl custom HTTP requests with custom headers
  • file chunking
  • Google Drive API resumable upload
  • passing JSON encoded requests in body
  • exponential backoff

Doesn’t sound like much but it took a while to piece it all together.

54 Replies to “Google drive API file upload script”

  1. Ben, with the API being now (hopefully) further develloped.

    Do you feel your script still holds sufficient value after 5 years ?
    Or should we use the drive API v3 these days ?

    1. Well, I’m not even aware of a v2 sunset date. My bet is that Google itself has quite a few dependencies on it. So I don’t know, if you want your script to run for the next 10 years, sure you should probably use v3. If you’re learning the API and setting up something new, yeah, might as well learn v3. Otherwise v2 is just fine.

      In my experience there are only minor differences between v2 and v3. This script is still a good reference point if you want to build something v3 from the ground up. Some of the concepts and implementations are the same.

    1. Oh, I see, on line 38:

      $folder_id = “” ;

      one needs ot copy a hash value from the url of the folder where one wants to upload the file and put inside the quotes.

  2. Hi Ben.

    I am pretty new to programming. Right now i am trying to make an app which records a video and uploads it to the Google Drive, but i can only manage to take pictures and upload not videoes

    Maybe you would look into it?

    Here is my script

    1. Take a look at the create_google_file() function, right now it returns the location of where to upload the file. Either this location has the file ID which you can use to build a client accessible URL, or maybe you can improve the function such that it returns more information about the file it just created than just the upload location.

      If you look at the API call that the function makes: you’ll see that what is returned is a complete file ressource ( which will contain all the properties about a file.

      I hope this helps.

  3. Hi ,
    I am Getting this error
    “usage: [folder_id] where is the full path to the file that you want to upload to Google Drive. and [folder_id] is the the folder where you want to upload the file (optional, defaults to root)”

    can u pls help me ?

    1. It looks like you are trying to call this from a web browser. This is a script meant to be called from the command line. You could probably tweak it to be ran from the web browser but you’ll have to get your hands dirty 🙂 Take a look at all the arguments given with $argv and try to hardcode them instead, or get them through $_GET or $_POST variables maybe.

      Good luck!

  4. Is there any way to login in google through back end script, without any user interface .
    Actual i want to upload file in google drive without any user login .
    anonymous user also can upload file in same google drive .
    Is it possible ? If possible how .
    please contact me in email.

    1. And thanks for improving it! I’ve been reluctant to update parse_response() so far hoping that Google’s erroneous behavior was temporary but it’s been about a year now so maybe we should just get used to it :).

  5. Salute to you saved my day.
    If you would have provided the option to donate i would have done that too.
    Thanks !!!!!

  6. It took some doing, but using refresh token generation contributed by James above and removing the mime types and I got this working. Thank you so much, you guys really helped me out!

  7. To improve my last comment (i let u fusion),
    My header is good (without range anyway), i’ve a code (100 stange code by the way), but my body response looks like :
    HTTP /1.1 308 Resume Incomplete
    Range: bytes=0-524288
    X-Range-MD5 ….

    Maybe i make a mistake and that’s why Youtube gives me two response code. Anyway my upload works but if you can help me to figure out i really apreciate it.

    Thanks again 😉

    1. Yeah so… this might not be the answer you are looking for but I’m pretty sure that it’s a bug on Google’s side.

      As far as I know you should never ever get 2 response codes back from an HTTP server. So that’s kind of a dead giveaway that something is fishy; now I’m guilty of not reporting the bug to them. We have a few other issues we’ve told them about and they move at a glacial pace.

      So as far as fixing your problem, it’s simple, if you see 2 response codes, disregard the first one :). The reason my code does not account for this phenomenon is that they actually didn’t have this erroneous behavior previously. It was introduced a few months ago and I just hardcoded around it.

      I hope this helps.

      1. Hi,
        I found by looking over and over on diffirent API like Facebook, or Dailymotion and i found this line in the middle of a request function :
        // disable the ‘Expect: 100-continue’ behaviour. This causes CURL to wait for 2 seconds if the server does not support this header.
        $existing_headers[] = ‘Expect:’;

        By the way, i fixe the problem like u said by avoid first response.

        Thank you again you are awesome 😉

  8. Hi mate,
    First of all, i want to thank you for this awesome job.

    I use cURL and PHP for youtube upload and i’ve a problem with the parsed_response function.
    I think it’s not the same thing that Google drive because youtube anwser me strange thing.
    In fact, Range are not in headers, and i receive two Code : 100 Continue just before a 308 Resume Incomplete.

    Can you help me to improve this function ?

    Thanks in advance

  9. Thanks for the script, I am really in need of this right now, but I am having a problem, I get this when i run from the command line

    > mime type detected: text/plain
    > retrieving access token
    > getting new one
    PHP Notice: Undefined property: stdClass::$access_token in /mnt/hgfs/shared/PROJECTS/ on line 254
    ERROR: problems getting an access token

    any help is greatly appreciated, thanks

    1. So, like many others here, you are having token issues 🙂

      How did you go about retrieving the refresh token that you plugged in the script?

    1. Interesting! Yeah mime types are hardcoded here, it’s curious you got away with just removing them.

      Thanks for following up with solutions & improvements, I’m sure it will help someone.

  10. Nice script but I am getting this error:
    > mime type detected: regular file
    > retrieving access token
    > from cache
    > creating file with Google
    ERROR: could not create resumable file
    [code] => 400
    [headers] => Array
    [server] => HTTP Upload Server Built on Apr 8 2013 13:06:58 (1365451618)
    [content-type] => application/json
    [date] => Mon, 22 Apr 2013 20:03:13 GMT
    [pragma] => no-cache
    [expires] => Fri, 01 Jan 1990 00:00:00 GMT
    [cache-control] => no-cache, no-store, must-revalidate
    [content-length] => 285

    [body] => {
    "error": {
    "errors": [
    "domain": "global",
    "reason": "badContent",
    "message": "Media type 'regular file' is not supported. Valid media types: [*/*]"
    "code": 400,
    "message": "Media type 'regular file' is not supported. Valid media types: [*/*]"

        1. Also noticed that the access token changes everytime I rm /tmp/access_token_* … I thought it has a lifetime of 3600s..

          1. It does have a lifetime, that is how long you can keep using it until Google asks you to get another one with your refresh token.

            That being said, if you ask for another access token before the current one expires, Google is happy to oblige even before it expires (I believe the old one then becomes invalid).

        2. And you are uploading an actual mp4 video? Setting up your tokens is supposed to be the hardest part, at least you have that behind you 🙂

    1. Use this script:

      Add a line to print_r( $accessToken ) after it has been retrieved.

      It will contain the refresh token as well, except it will be JSON encoded so make sure to json_decode() it before using it in my script.

      Super tricky & convoluted, there might be a better way to retrieve a refresh token but that’s the only process I managed to get working. At least you only have to do it once for the refresh token.

  11. Looks great thanks for doing this. Top quality guy 🙂 I’m a little bit of a programming newbie, can’t figure out how to solve this error I keep getting:

    So far I’ve:
    >Via google API console created an installed application
    > Got the Client/ID secret from that
    > The refresh token is where I’m a bit confused, I used the following to set the original refresh token:{myid}&

    But maybe this is where I’m going wrong?

    And here is the error:
    root@199:/var/www# php drive.php worker.php
    > mime type detected: text/x-php
    > retrieving access token
    > getting new one
    PHP Notice: Undefined property: stdClass::$access_token in /var/www/drive.php on line 253
    ERROR: problems getting an access token

    1. The refresh/access token handling logistic is very convoluted and had me confused and taking guesses for the longest time. It’s ok to bang your head against the wall a bit trying to figure it out.

      So you’ve set the configuration variables at the top of the file right? ($client_id, $client_secret & $refresh_token)

      Where did you get them from? Which process did you follow?

      It’s important because there are different types of client IDs you can create and also because of encoding.

      1. Cheers mate, makes me feel a little better about all this head banging 🙂

        Yeah, all these values have been set. I got the $client_id, $client_secret values from the Google Console > API Access and created a “Client ID for installed applications”.

        The $refresh_token was the confusing bit, the only way I got this was by trying to install the php sample upload code provided in the api documentation -that didn’t work for me either but it spat out a URL that had a refresh token and that’s the one I used!

        Appreciate the help!

        1. Just read that comment back and it sounded like I solved it, but still banging my head haha. Must be an issue with the refresh token if you have the same console api setup as me, how did you get your token? any ideas where this might be going wrong buddy?

            1. Unlikely because it was provided via a URL and appeared in an input box.

              I’ll take another stab at it tonight and see if I get anywhere, you should work for google man, this whole process is a huge headache!

              – Thanks

              1. It really is a headache, that’s why I went out of my way to publish this online.

                Can I ask you for exactly where you got your refresh token from? I can try and reproduce these steps on my end and see what I get.

                As noted in my reply to Jakup below, I followed a pretty unorthodox process to get mine.

  12. thank you for this program 🙂
    unfortunately though, i am having a problem whenever i run this on my browser. this error appears:

    Notice: Undefined variable: argv in C:xampphtdocsgoogle_drive_uploaderindex.php on line 28

    Notice: Undefined variable: argv in C:xampphtdocsgoogle_drive_uploaderindex.php on line 29
    usage: [folder_id] where is the full path to the file that you want to upload to Google Drive. and [folder_id] is the the folder where you want to upload the file (optional, defaults to root)

    how could i solve this? please and thank you!

      1. Hey,

        are you running this as a console script? It looks like you’re loading it as a webpage. When you run a PHP script manually you can pass extra parameters to the command you use to invoke your script. They end up in a super variable called “argv”, this is common across several programming languages.

        In the case of the Google Drive uploader, I’m counting on the folder ID to be passed to the script as a parameter so it knows where to upload the file.

        Your script should be invoked as such:
        ./google_drive_uploader.php [folder_id]

        Having this behind a web servers means you can’t pass parameters via argv hence the undefined variable error. If you absolutely need to run this from a web server you will want to rewrite it a bit so it takes its folder id from somewhere else, maybe a $_GET variable, maybe hardcode it if it’s static.

        I hope this helps.

Leave a Reply

Your email address will not be published. Required fields are marked *