Upload file to Google Drive with PHP/cURL and API REST v3 (without Client Library)

What?
So this is supposed to be a quick article detailing how I used a PHP function to upload a video file to Google Drive. Note that this example does not involve installing the Google Client Library for PHP. So no classes or built-in functions that you didn't write or know what they're doing.

Why?
The objective here is to upload a video file to my Google Drive. This assumes you have already "Enabled the Drive API" via your Google Developers console as well as gotten your OAuth 2.0 access/refresh tokens and specified the appropriate scopes.

The scopes I chose were to do with getting a working example rather than the recommended scope, though you may only need the first (untested):
https://www.googleapis.com/auth/drive.file
https://www.googleapis.com/auth/userinfo.email
https://www.googleapis.com/auth/userinfo.profile

How?
I've listed below 3 methods, the first follows the manual (or the logic thereof as I don't use the client library) and is a function sending two requests, the second is the appropriate multipart function which uploads and names the file in a single request, and the third is a 'nice-to-have' upload to the specified folder.

Method #1: two requests
1. Send the media file
2. Update the name of the file you just uploaded
$GAPIS = 'https://www.googleapis.com/';

function uploadFile($access_token, $file, $mime_type, $name) {
    global $GAPIS;

    $ch1 = curl_init();

    // don't do ssl checks
    curl_setopt($ch1, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch1, CURLOPT_SSL_VERIFYHOST, false);

    // do other curl stuff
    curl_setopt($ch1, CURLOPT_URL, $GAPIS . 'upload/drive/v3/files?uploadType=media');
    curl_setopt($ch1, CURLOPT_BINARYTRANSFER, 1);
    curl_setopt($ch1, CURLOPT_POST, 1);
    curl_setopt($ch1, CURLOPT_POSTFIELDS, file_get_contents($file));
    curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true);

    // set authorization header
    curl_setopt($ch1, CURLOPT_HTTPHEADER, array('Content-Type: '.$mime_type, 'Content-Length: ' . filesize($file), 'Authorization: Bearer ' . $access_token) );

    // execute cURL request
    $response=curl_exec($ch1);
    if($response === false){
        $output = 'ERROR: '.curl_error($ch1);
    } else{
        $output = $response;
    }

    // close first request handler
    curl_close($ch1);

    // now let's get the ID of the file we just created
    // and submit the file metadata
    $this_response_arr = json_decode($response, true);

    if(isset($this_response_arr['id'])){
        $this_file_id = $this_response_arr['id'];

        $ch2 = curl_init();

        // don't do ssl checks
        curl_setopt($ch2, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch2, CURLOPT_SSL_VERIFYHOST, false);

        // do other curl stuff
        curl_setopt($ch2, CURLOPT_URL, $GAPIS . 'drive/v3/files/'.$this_file_id);
        curl_setopt($ch2, CURLOPT_CUSTOMREQUEST, 'PATCH');

        // initialize fields to submit
        $post_fields = array();

        // remove extension
        $this_file_name = explode('.', $name);

        // submit name field
        $post_fields['name']=$this_file_name[0];

        curl_setopt($ch2, CURLOPT_POSTFIELDS, json_encode($post_fields));

        // return response as a string
        curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true);

        // set authorization header
        curl_setopt($ch2, CURLOPT_HTTPHEADER, array('Content-Type: application/json', 'Authorization: Bearer ' . $access_token) );

        // execute cURL request
        $response=curl_exec($ch2);
        if($response === false){
            $output = 'ERROR: '.curl_error($ch2);
        } else{
            $output = $response;
        }

        // close second request handler
        curl_close($ch2);
    }

    return $output;
}

Usage:
$mime_type = 'video/mp4';  // could be 'image/jpeg', etc.
$new_name = 'My Video';
$the_file_and_path = '../videos/my_video.mp4';
$result = uploadFile($my_access_token, $the_file_and_path, $mime_type, $new_name);
This method was the first one I could get working but it's a bit excessive having to send two requests (albeit in one function) especially with imposed limit requests. I can halve this so let's upgrade!

Method #2: Mulitipart single request
This is my recommended version. In this function, we are sending the metadata before the media:
$GAPIS = 'https://www.googleapis.com/';

function uploadFile($access_token, $file, $mime_type, $name) {
    global $GAPIS;

    $ch1 = curl_init();

    // don't do ssl checks
    curl_setopt($ch1, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch1, CURLOPT_SSL_VERIFYHOST, false);

    $my_beautiful_body = '
--joes_awesome_divider
Content-Type: application/json; charset=UTF-8

{
    "name": "'.$name.'"
}

--joes_awesome_divider
Content-Type: '.$mime_type.'

'.file_get_contents($file).'

--joes_awesome_divider--
    ';

    // do other curl stuff
    curl_setopt($ch1, CURLOPT_URL, $GAPIS . 'upload/drive/v3/files?uploadType=multipart');
    curl_setopt($ch1, CURLOPT_POST, 1);
    curl_setopt($ch1, CURLOPT_POSTFIELDS, $my_beautiful_body);
    curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true);

    // set authorization header
    curl_setopt($ch1, CURLOPT_HTTPHEADER, array('Content-Type: multipart/related; boundary=joes_awesome_divider', 'Authorization: Bearer ' . $access_token) );

    // execute cURL request
    $response=curl_exec($ch1);
    if($response === false){
        $output = 'ERROR: '.curl_error($ch1);
    } else{
        $output = $response;
    }

    // close first request handler
    curl_close($ch1);

    return $output;
}

Usage:
$mime_type = 'video/mp4';  // could be 'image/jpeg', etc.
$new_name = 'My Video';
$the_file_and_path = '../videos/my_video.mp4';
$result = uploadFile($my_access_token, $the_file_and_path, $mime_type, $new_name);

Method #3: As method 2 but with specified folder

To get the folder ID, simply browse to the Google Drive folder in your browser and note the last string in the URL. So in https://drive.google.com/drive/folders/<SomeAlphaNumericCharactersAndThenSome>, the value where "<SomeAlphaNumericCharactersAndThenSome>" sits will be your folder ID.

$GAPIS = 'https://www.googleapis.com/';

function uploadFile($access_token, $file, $mime_type, $name, $folder) {
    global $GAPIS;

    $ch1 = curl_init();

    // don't do ssl checks
    curl_setopt($ch1, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch1, CURLOPT_SSL_VERIFYHOST, false);

    $my_beautiful_body = '
--joes_awesome_divider
Content-Type: application/json; charset=UTF-8

{
    "name": "'.$name.'",
    "parents": ["'.$folder.'"]
}

--joes_awesome_divider
Content-Type: '.$mime_type.'

'.file_get_contents($file).'

--joes_awesome_divider--
    ';

    // do other curl stuff
    curl_setopt($ch1, CURLOPT_URL, $GAPIS . 'upload/drive/v3/files?uploadType=multipart');
    curl_setopt($ch1, CURLOPT_POST, 1);
    curl_setopt($ch1, CURLOPT_POSTFIELDS, $my_beautiful_body);
    curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true);

    // set authorization header
    curl_setopt($ch1, CURLOPT_HTTPHEADER, array('Content-Type: multipart/related; boundary=joes_awesome_divider', 'Authorization: Bearer ' . $access_token) );

    // execute cURL request
    $response=curl_exec($ch1);
    if($response === false){
        $output = 'ERROR: '.curl_error($ch1);
    } else{
        $output = $response;
    }

    // close first request handler
    curl_close($ch1);

    return $output;
}


Usage:
$mime_type = 'video/mp4';
$new_name = 'My Video';
$the_file_and_path = '../videos/my_video.mp4';
$folder_id = 'l4S9F71BMqlXWtl4uGvy2etd_hrNwmroi';
$result = uploadFile($my_access_token, $the_file_and_path, $mime_type, $new_name, $folder_id);


Response(s)
The response should be a JSON with the file metadata (yours will be different but this is just an example). You may want to extract the ID so that you can manage the file further.
{
 "kind": "drive#file",
 "id": "1srXLU-lotsofalphanumericcharacters_",
 "name": "my_video.mp4",
 "mimeType": "video/mp4"
}

Additional:
Why not? Let's add a description and some security where we disable download, sharing, etc. to the viewers (except the owner). In other words, ticking the owner settings under the advanced sharing settings programmatically:

  • Prevent editors from changing access and adding new people :: Set by "writersCanShare" = false
  • Disable options to download, print and copy for commenters and viewers :: Set by "copyRequiresWriterPermission" = true

Google Drive: Owner Settings: Disable Sharing

To do this, set the metadata to the following in the above function:
{
    "name": "'.$name.'",
    "parents": ["'.$folder.'"],
    "description": "'.$desc.'",
    "copyRequiresWriterPermission": true,
    "writersCanShare": false
}
Reminder: if you are adding the description, then pass this as a parameter to the above function.


Source(s):

Related Articles

Joes Revolver Map

Joes Word Cloud

Accreditation

Badge - Certified Zoho Creator Associate
Badge - Certified Zoho Creator Associate

Donate & Support

If you like my content, and would like to support this sharing site, feel free to donate using a method below:

Paypal:
Donate to Joel Lipman via PayPal

Bitcoin:
Donate to Joel Lipman with Bitcoin - Valid till 8 May 2022 3QnhmaBX7LQSRsC9hh6Je9rGQKEGNQNfPb
© 2021 Joel Lipman .com. All Rights Reserved.