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):
copyraw
https://www.googleapis.com/auth/drive.file
https://www.googleapis.com/auth/userinfo.email
https://www.googleapis.com/auth/userinfo.profile
  1.  https://www.googleapis.com/auth/drive.file 
  2.  https://www.googleapis.com/auth/userinfo.email 
  3.  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
copyraw
$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;
}
  1.  $GAPIS = 'https://www.googleapis.com/'
  2.   
  3.  function uploadFile($access_token, $file, $mime_type, $name) { 
  4.      global $GAPIS
  5.   
  6.      $ch1 = curl_init()
  7.   
  8.      // don't do ssl checks 
  9.      curl_setopt($ch1, CURLOPT_SSL_VERIFYPEER, false)
  10.      curl_setopt($ch1, CURLOPT_SSL_VERIFYHOST, false)
  11.   
  12.      // do other curl stuff 
  13.      curl_setopt($ch1, CURLOPT_URL, $GAPIS . 'upload/drive/v3/files?uploadType=media')
  14.      curl_setopt($ch1, CURLOPT_BINARYTRANSFER, 1)
  15.      curl_setopt($ch1, CURLOPT_POST, 1)
  16.      curl_setopt($ch1, CURLOPT_POSTFIELDS, file_get_contents($file))
  17.      curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true)
  18.   
  19.      // set authorization header 
  20.      curl_setopt($ch1, CURLOPT_HTTPHEADER, array('Content-Type: '.$mime_type, 'Content-Length: ' . filesize($file), 'Authorization: Bearer ' . $access_token) )
  21.   
  22.      // execute cURL request 
  23.      $response=curl_exec($ch1)
  24.      if($response === false){ 
  25.          $output = 'ERROR: '.curl_error($ch1)
  26.      } else{ 
  27.          $output = $response
  28.      } 
  29.   
  30.      // close first request handler 
  31.      curl_close($ch1)
  32.   
  33.      // now let's get the ID of the file we just created 
  34.      // and submit the file metadata 
  35.      $this_response_arr = json_decode($response, true)
  36.   
  37.      if(isset($this_response_arr['id'])){ 
  38.          $this_file_id = $this_response_arr['id']
  39.   
  40.          $ch2 = curl_init()
  41.   
  42.          // don't do ssl checks 
  43.          curl_setopt($ch2, CURLOPT_SSL_VERIFYPEER, false)
  44.          curl_setopt($ch2, CURLOPT_SSL_VERIFYHOST, false)
  45.   
  46.          // do other curl stuff 
  47.          curl_setopt($ch2, CURLOPT_URL, $GAPIS . 'drive/v3/files/'.$this_file_id)
  48.          curl_setopt($ch2, CURLOPT_CUSTOMREQUEST, 'PATCH')
  49.   
  50.          // initialize fields to submit 
  51.          $post_fields = array()
  52.   
  53.          // remove extension 
  54.          $this_file_name = explode('.', $name)
  55.   
  56.          // submit name field 
  57.          $post_fields['name']=$this_file_name[0]
  58.   
  59.          curl_setopt($ch2, CURLOPT_POSTFIELDS, json_encode($post_fields))
  60.   
  61.          // return response as a string 
  62.          curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true)
  63.   
  64.          // set authorization header 
  65.          curl_setopt($ch2, CURLOPT_HTTPHEADER, array('Content-Type: application/json', 'Authorization: Bearer ' . $access_token) )
  66.   
  67.          // execute cURL request 
  68.          $response=curl_exec($ch2)
  69.          if($response === false){ 
  70.              $output = 'ERROR: '.curl_error($ch2)
  71.          } else{ 
  72.              $output = $response
  73.          } 
  74.   
  75.          // close second request handler 
  76.          curl_close($ch2)
  77.      } 
  78.   
  79.      return $output
  80.  } 

Usage:
copyraw
$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);
  1.  $mime_type = 'video/mp4';  // could be 'image/jpeg', etc. 
  2.  $new_name = 'My Video'
  3.  $the_file_and_path = '../videos/my_video.mp4'
  4.  $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:
copyraw
$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;
}
  1.  $GAPIS = 'https://www.googleapis.com/'
  2.   
  3.  function uploadFile($access_token, $file, $mime_type, $name) { 
  4.      global $GAPIS
  5.   
  6.      $ch1 = curl_init()
  7.   
  8.      // don't do ssl checks 
  9.      curl_setopt($ch1, CURLOPT_SSL_VERIFYPEER, false)
  10.      curl_setopt($ch1, CURLOPT_SSL_VERIFYHOST, false)
  11.   
  12.      $my_beautiful_body = ' 
  13.  --joes_awesome_divider 
  14.  Content-Type: application/json; charset=UTF-8 
  15.   
  16.  { 
  17.      "name": "'.$name.'" 
  18.  } 
  19.   
  20.  --joes_awesome_divider 
  21.  Content-Type: '.$mime_type.' 
  22.   
  23.  '.file_get_contents($file).' 
  24.   
  25.  --joes_awesome_divider-- 
  26.      '
  27.   
  28.      // do other curl stuff 
  29.      curl_setopt($ch1, CURLOPT_URL, $GAPIS . 'upload/drive/v3/files?uploadType=multipart')
  30.      curl_setopt($ch1, CURLOPT_POST, 1)
  31.      curl_setopt($ch1, CURLOPT_POSTFIELDS, $my_beautiful_body)
  32.      curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true)
  33.   
  34.      // set authorization header 
  35.      curl_setopt($ch1, CURLOPT_HTTPHEADER, array('Content-Type: multipart/related; boundary=joes_awesome_divider', 'Authorization: Bearer ' . $access_token) )
  36.   
  37.      // execute cURL request 
  38.      $response=curl_exec($ch1)
  39.      if($response === false){ 
  40.          $output = 'ERROR: '.curl_error($ch1)
  41.      } else{ 
  42.          $output = $response
  43.      } 
  44.   
  45.      // close first request handler 
  46.      curl_close($ch1)
  47.   
  48.      return $output
  49.  } 

Usage:
copyraw
$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);
  1.  $mime_type = 'video/mp4';  // could be 'image/jpeg', etc. 
  2.  $new_name = 'My Video'
  3.  $the_file_and_path = '../videos/my_video.mp4'
  4.  $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.

copyraw
$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;
}
  1.  $GAPIS = 'https://www.googleapis.com/'
  2.   
  3.  function uploadFile($access_token, $file, $mime_type, $name, $folder) { 
  4.      global $GAPIS
  5.   
  6.      $ch1 = curl_init()
  7.   
  8.      // don't do ssl checks 
  9.      curl_setopt($ch1, CURLOPT_SSL_VERIFYPEER, false)
  10.      curl_setopt($ch1, CURLOPT_SSL_VERIFYHOST, false)
  11.   
  12.      $my_beautiful_body = ' 
  13.  --joes_awesome_divider 
  14.  Content-Type: application/json; charset=UTF-8 
  15.   
  16.  { 
  17.      "name": "'.$name.'", 
  18.      "parents": ["'.$folder.'"] 
  19.  } 
  20.   
  21.  --joes_awesome_divider 
  22.  Content-Type: '.$mime_type.' 
  23.   
  24.  '.file_get_contents($file).' 
  25.   
  26.  --joes_awesome_divider-- 
  27.      '
  28.   
  29.      // do other curl stuff 
  30.      curl_setopt($ch1, CURLOPT_URL, $GAPIS . 'upload/drive/v3/files?uploadType=multipart')
  31.      curl_setopt($ch1, CURLOPT_POST, 1)
  32.      curl_setopt($ch1, CURLOPT_POSTFIELDS, $my_beautiful_body)
  33.      curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true)
  34.   
  35.      // set authorization header 
  36.      curl_setopt($ch1, CURLOPT_HTTPHEADER, array('Content-Type: multipart/related; boundary=joes_awesome_divider', 'Authorization: Bearer ' . $access_token) )
  37.   
  38.      // execute cURL request 
  39.      $response=curl_exec($ch1)
  40.      if($response === false){ 
  41.          $output = 'ERROR: '.curl_error($ch1)
  42.      } else{ 
  43.          $output = $response
  44.      } 
  45.   
  46.      // close first request handler 
  47.      curl_close($ch1)
  48.   
  49.      return $output
  50.  } 


Usage:
copyraw
$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);
  1.  $mime_type = 'video/mp4'
  2.  $new_name = 'My Video'
  3.  $the_file_and_path = '../videos/my_video.mp4'
  4.  $folder_id = 'l4S9F71BMqlXWtl4uGvy2etd_hrNwmroi'
  5.  $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.
copyraw
{
 "kind": "drive#file",
 "id": "1srXLU-lotsofalphanumericcharacters_",
 "name": "my_video.mp4",
 "mimeType": "video/mp4"
}
  1.  { 
  2.   "kind": "drive#file", 
  3.   "id": "1srXLU-lotsofalphanumericcharacters_", 
  4.   "name": "my_video.mp4", 
  5.   "mimeType": "video/mp4" 
  6.  } 

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:
copyraw
{
    "name": "'.$name.'",
    "parents": ["'.$folder.'"],
    "description": "'.$desc.'",
    "copyRequiresWriterPermission": true,
    "writersCanShare": false
}
  1.  { 
  2.      "name": "'.$name.'", 
  3.      "parents": ["'.$folder.'"], 
  4.      "description": "'.$desc.'", 
  5.      "copyRequiresWriterPermission": true, 
  6.      "writersCanShare": false 
  7.  } 
Reminder: if you are adding the description, then pass this as a parameter to the above function.


Source(s):
Category: Google :: Article: 649

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.