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
- 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
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; }
- $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:
copyraw
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!$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);
- $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 #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; }
- $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:
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);
- $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.
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; }
- $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:
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);
- $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.
copyraw
{ "kind": "drive#file", "id": "1srXLU-lotsofalphanumericcharacters_", "name": "my_video.mp4", "mimeType": "video/mp4" }
- {
- "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

To do this, set the metadata to the following in the above function:
copyraw
Reminder: if you are adding the description, then pass this as a parameter to the above function.{ "name": "'.$name.'", "parents": ["'.$folder.'"], "description": "'.$desc.'", "copyRequiresWriterPermission": true, "writersCanShare": false }
- {
- "name": "'.$name.'",
- "parents": ["'.$folder.'"],
- "description": "'.$desc.'",
- "copyRequiresWriterPermission": true,
- "writersCanShare": false
- }
Source(s):
- Google Drive API v3
- Google OAuth 2.0 Playground
- Google Developers Console - APIs
- RFC 2387 - The MIME Multipart/Related Content-type
- Google Drive API v3 | Files Update
- JoelLipman.com - Google Authentication - OAuth 2.0 using PHP/cURL
Category: Google :: Article: 649
Add comment