How to Integrate Google Drive API with PHP – Upload/Download/Delete File on Drive

Recently, I worked on a project where we’re dealing with the Google Drive API. We performed a few tasks like creating a folder, uploading a file and much more on Google Drive. As I did some work with Drive API, I thought it’s better to write an article on this topic. So here I show you how to integrate Google Drive API with PHP. For integration, I am going to cover the following topics.

  • Create a folder on Drive.
  • Upload a file to Google Drive.
  • Download a file from Google Drive.
  • Share a folder with someone.
  • Delete a file from Drive.

To interact with the Drive API, you require to register your application on Google Console. Upon creating an application, you’ll get API credentials which will be used to build the Google OAuth flow. By following the OAuth, we’ll generate an access token that is required to send in each API request.

Register an Application and Create Credentials

  • Go to the Google Developer Console.
  • Create a new project. You can also select existing projects.
  • Give a name to your project. Google Console will generate a unique project ID for it.
  • Your project will appear on top of the left sidebar.
  • Click on Library and search for Drive API. Enable it.
  • Click on the Credentials. Select Oauth Client id under Create credentials. Choose the radio button for the Web Application.
  • Give the Name. Under Authorized JavaScript origins enter your domain URL. In the Authorized redirect URIs add the link of the redirect URL. In my case, I passed the URL http://localhost/drive-api/callback.php. I’ll create this file later.
  • Click on the Create button. You will get the client ID and client secret in the pop-up. Copy these details. We will need it in a moment.
drive-api-application

Create a Database and DB Class(class-db.php)

When we’ll run the OAuth process, we get the access token that should store in the database. I’ll store token details in the oauth_tokens table. Create the oauth_tokens table in the database using the below SQL.

CREATE TABLE `oauth_tokens` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `provider` varchar(255) NOT NULL,
  `provider_value` text NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

The access token has an expiry time, so it needs to regenerate in the background and update new details in the database. To perform this process, let’s create class-db.php file and write code to handle this stuff.

In the class-db.php file, I’ll write a few methods each one has a specific responsibility.

  • get_access_token() : This method will give the user’s access token. Apart from the access token, it also has values of refresh_token and expires_in.
  • get_refersh_token() : Extract a refresh_token from the column’s value. This token is required to regenerate the access token.
  • update_access_token() : It will insert or update the access token of the users in the database table.
<?php
class DB {
    private $dbHost     = "DB_HOST";
    private $dbUsername = "DB_USERNAME";
    private $dbPassword = "DB_PASSWORD";
    private $dbName     = "DB_NAME";
   
    public function __construct(){
        if(!isset($this->db)){
            // Connect to the database
            $conn = new mysqli($this->dbHost, $this->dbUsername, $this->dbPassword, $this->dbName);
            if($conn->connect_error){
                die("Failed to connect with MySQL: " . $conn->connect_error);
            }else{
                $this->db = $conn;
            }
        }
    }
   
    public function is_table_empty() {
        $result = $this->db->query("SELECT id FROM oauth_tokens WHERE provider = 'google'");
        if($result->num_rows) {
            return false;
        }
   
        return true;
    }
   
    public function get_access_token() {
        $sql = $this->db->query("SELECT provider_value FROM oauth_tokens WHERE provider = 'google'");
        $result = $sql->fetch_assoc();
        return json_decode($result['provider_value']);
    }
   
    public function get_refersh_token() {
        $result = $this->get_access_token();
        return $result->refresh_token;
    }
   
    public function update_access_token($token) {
        if($this->is_table_empty()) {
            $sql = sprintf("INSERT INTO oauth_tokens(provider, provider_value) VALUES('%s', '%s')", 'google', $this->db->real_escape_string($token));
            $this->db->query($sql);
        } else {
            $sql = sprintf("UPDATE oauth_tokens SET provider_value = '%s' WHERE provider = '%s'", $this->db->real_escape_string($token), 'google');
            $this->db->query($sql);
        }
    }
}

Install Hybridauth and Google’s PHP Library

To build the Google OAuth flow seamlessly, I’ll use the Hybridauth library. While using this library, we just need to configure the settings and the rest work will be handled by the library itself.

Along with HybridAuth, I’ll also install Google’s PHP(google/apiclient) library that allows interaction with Google Drive.

Create a composer.json file and add the following lines to it.

{
    "require": {
        "google/apiclient": "^2.12.1",
        "hybridauth/hybridauth" : "~3.0"
    },
    "scripts": {
        "pre-autoload-dump": "Google\\Task\\Composer::cleanup"
    },
    "extra": {
        "google/apiclient-services": [
            "Drive"
        ]
    }
}

Here, I used the Composer’s cleanup task. This is because when you install the google/apiclient package, it downloads all the services(more than 200+) of Google. But we want only Google Drive service. The cleanup will remove all other services and keep only Drive.

After this, open the command prompt and run the command:

composer install

This command will install the Hybridauth and google/apiclient packages into your project directory.

Generate Access Token

Hyridauth library requires passing a few values to the configuration array like API credentials, redirect URL, and scope. For this, create a config.php file and use the code below in it.

<?php
require_once 'vendor/autoload.php';
require_once 'class-db.php';
 
define('GOOGLE_CLIENT_ID', 'PASTE_CLIENT_ID_HERE');
define('GOOGLE_CLIENT_SECRET', 'PASTE_CLIENT_SECRET_HERE');
 
$config = [
    'callback' => 'http://localhost/drive-api/callback.php',
    'keys'     => [
                    'id' => GOOGLE_CLIENT_ID,
                    'secret' => GOOGLE_CLIENT_SECRET
                ],
    'scope'    => 'https://www.googleapis.com/auth/drive',
    'authorize_url_parameters' => [
            'approval_prompt' => 'force', // to pass only when you need to acquire a new refresh token.
            'access_type' => 'offline'
    ]
];
 
$adapter = new Hybridauth\Provider\Google( $config );

In the above code, I passed my callback URL which should be changed in your case. I set the https://www.googleapis.com/auth/drive scope that gives access to our application to perform API operations on Google Drive. Replace the other placeholders with their actual values.

Next, create a callback.php file that initiates the OAuth, and stores the access token once the authorization is successful.

<?php
require_once 'config.php';
 
try {
    $adapter->authenticate();
    $token = $adapter->getAccessToken();
    $db = new DB();
    $db->update_access_token(json_encode($token));
    echo "Access token inserted successfully.";
}
catch( Exception $e ){
    echo $e->getMessage() ;
}

Head over to the browser and run this callback.php, you will be asked for Google authentication, complete it and you will see the access token get stored in the database. It means you are ready to interact with the Google Drive API.

Create a Folder on Google Drive

With the help of the access token and Google’s PHP package, you can run different operations on Google Drive. Let’s say you want to create a new folder on the Drive. It requires the following steps to be performed in the code.

  • Include a configuration file that gives the access token and access to Google API services.
  • Use mimeType as application/vnd.google-apps.folder to create a directory on a Drive.
  • Regenerate the access token if it expires and re-initiate the task. This must be done in the background.

Having said that, let’s create a create-folder.php file and place the below code into it.

<?php
require_once 'config.php';
 
create_drive_folder('apifolder'); // folder name 'apifolder'
 
function create_drive_folder($folder) {
 
    $client = new Google_Client();
 
    $db = new DB();
 
    $arr_token = (array) $db->get_access_token();
    $accessToken = array(
        'access_token' => $arr_token['access_token'],
        'expires_in' => $arr_token['expires_in'],
    );
 
    $client->setAccessToken($accessToken);

    $service = new Google\Service\Drive($client);

    try {
        // Create a folder in root
        $postBody = new Google\Service\Drive\DriveFile([
            'name' => $folder,
            'mimeType' => 'application/vnd.google-apps.folder',
        ]);

        $result = $service->files->create($postBody);
        echo "Folder is created successfully.";
    } catch(Exception $e) {
        if( 401 == $e->getCode() ) {
            $refresh_token = $db->get_refersh_token();
 
            $client = new GuzzleHttp\Client(['base_uri' => 'https://accounts.google.com']);
 
            $response = $client->request('POST', '/o/oauth2/token', [
                'form_params' => [
                    "grant_type" => "refresh_token",
                    "refresh_token" => $refresh_token,
                    "client_id" => GOOGLE_CLIENT_ID,
                    "client_secret" => GOOGLE_CLIENT_SECRET,
                ],
            ]);
 
            $data = (array) json_decode($response->getBody());
            $data['refresh_token'] = $refresh_token;
 
            $db->update_access_token(json_encode($data));
 
            create_drive_folder($folder);
        } else {
            echo $e->getMessage(); //print the error
        }
    }
}

Here, I have passed the folder name as ‘apifolder’. Change the name of a directory to whatever you wish. Run this code and you should see this folder is created on Google Drive.

This code creates a directory in the root location of Drive. If you want to create a folder inside another directory then pass the id of the parent folder to the parents key as follows. 

// create a folder inside parent folder
$postBody = new Google\Service\Drive\DriveFile([
    'name' => 'apifolder',
    'parents' => ['PARENT_FOLDER_ID'],
    'mimeType' => 'application/vnd.google-apps.folder',
]);

If you are curious about how to get an id of the parent folder, click on the desired folder and copy the last string from the URL. It may have the format https://drive.google.com/drive/u/0/folders/ID_HERE.

Upload File to Google Drive

This is the most obvious task you may want to perform. People prefer to use the Google Drive service to store their files.

The below code will be used to upload any type of file on the Drive. For instance, I assume you have a 1.jpg file in your project’s directory. And you want to upload this image on a Drive. Change this filename as per your document. Also, pass the folder id under which this file should get uploaded.

<?php
require_once 'config.php';

$drive_folder_id = 'FOLDER_ID_HERE';
create_file_in_drive_folder($drive_folder_id);
 
function create_file_in_drive_folder($drive_folder_id) {
 
    $client = new Google_Client();
 
    $db = new DB();
 
    $arr_token = (array) $db->get_access_token();
    $accessToken = array(
        'access_token' => $arr_token['access_token'],
        'expires_in' => $arr_token['expires_in'],
    );
 
    $client->setAccessToken($accessToken);

    $service = new Google\Service\Drive($client);

    try {
        $file = getcwd(). '/1.jpg';
        $filename = basename($file);
        $filetype = mime_content_type($file);

        $resource = new Google\Service\Drive\DriveFile([
            'name' => $filename,
            'parents' => [$drive_folder_id],
        ]);

        $result = $service->files->create($resource, [
            'data' => file_get_contents($file),
            'mimeType' => $filetype,
            'uploadType' => 'multipart',
        ]);
        echo "File is uploaded successfully.";
    } catch(Exception $e) {
        if( 401 == $e->getCode() ) {
            $refresh_token = $db->get_refersh_token();
 
            $client = new GuzzleHttp\Client(['base_uri' => 'https://accounts.google.com']);
 
            $response = $client->request('POST', '/o/oauth2/token', [
                'form_params' => [
                    "grant_type" => "refresh_token",
                    "refresh_token" => $refresh_token,
                    "client_id" => GOOGLE_CLIENT_ID,
                    "client_secret" => GOOGLE_CLIENT_SECRET,
                ],
            ]);
 
            $data = (array) json_decode($response->getBody());
            $data['refresh_token'] = $refresh_token;
 
            $db->update_access_token(json_encode($data));
 
            create_file_in_drive_folder($drive_folder_id);
        } else {
            echo $e->getMessage(); //print the error
        }
    }
}

Upon running this code, your file should be found under the desired location. Just in case, if it fails we are printing the error which help you to get on track.

Share a Google Drive Folder

If you wish to share the Drive folder with a specific email, you can do so by creating permission for the folder. To create permission you have to pass 3 parameters – role, type, and emailAddress to the Permission resource. And then using the create() method of Permission class, your folder will be shared with the given email.

<?php
require_once 'config.php';
 
$drive_folder_id = 'FOLDER_ID_HERE';
share_drive_folder($drive_folder_id);
  
function share_drive_folder($drive_folder_id) {
  
    $client = new Google_Client();
  
    $db = new DB();
  
    $arr_token = (array) $db->get_access_token();
    $accessToken = array(
        'access_token' => $arr_token['access_token'],
        'expires_in' => $arr_token['expires_in'],
    );
  
    $client->setAccessToken($accessToken);
 
    
    try {
        $service = new Google\Service\Drive($client);

        $resource = new Google\Service\Drive\Permission([
            'type' => 'user',
            'role' => 'writer', // 'writer', 'reader'
            'emailAddress' => 'EMAIL_ADDRESS', // folder will be shared with this email
        ]);

        $result = $service->permissions->create($drive_folder_id, $resource);
        
        echo "Folder is shared successfully.";
    } catch(Exception $e) {
        if( 401 == $e->getCode() ) {
            $refresh_token = $db->get_refersh_token();
  
            $client = new GuzzleHttp\Client(['base_uri' => 'https://accounts.google.com']);
  
            $response = $client->request('POST', '/o/oauth2/token', [
                'form_params' => [
                    "grant_type" => "refresh_token",
                    "refresh_token" => $refresh_token,
                    "client_id" => GOOGLE_CLIENT_ID,
                    "client_secret" => GOOGLE_CLIENT_SECRET,
                ],
            ]);
  
            $data = (array) json_decode($response->getBody());
            $data['refresh_token'] = $refresh_token;
  
            $db->update_access_token(json_encode($data));
  
            share_drive_folder($drive_folder_id);
        } else {
            echo $e->getMessage(); //print the error
        }
    }
}

Download File from Google Drive

In this section, we write a code that will download the file from Google Drive and store it on your local system. I’ll download a file in chunks so there won’t be a problem even if the file is relatively large.

<?php
require_once 'config.php';

download_drive_file('DRIVE_FILE_NAME'); // say 1.jpg

function download_drive_file($filename = '') {

    $client = new Google_Client();

    $db = new DB();

    $arr_token = (array) $db->get_access_token();
    $accessToken = array(
        'access_token' => $arr_token['access_token'],
        'expires_in' => $arr_token['expires_in'],
    );

    $client->setAccessToken($accessToken);

    $service = new Google\Service\Drive($client);    

    try {
        // Check for $filename and include the file ID and size
        $files = $service->files->listFiles([
            'q' => "name='$filename'",
            'fields' => 'files(id,size)'
        ]);

        if(count($files) == 0) {
            echo "No files found.";
            return;
        }

        // Determine the file's size and ID
        $fileId = $files[0]->id;
        $fileSize = intval($files[0]->size);

        // Get the authorized Guzzle HTTP client
        $http = $client->authorize();

        // Open a file for writing
        $fp = fopen(time().'-'.$filename, 'w');

        // Download in 1 MB chunks
        $chunkSizeBytes = 1 * 1024 * 1024;
        $chunkStart = 0;

        // Iterate over each chunk and write it to our file
        while ($chunkStart < $fileSize) {
            $chunkEnd = $chunkStart + $chunkSizeBytes;
            $response = $http->request(
                'GET',
                sprintf('/drive/v3/files/%s', $fileId),
                [
                    'query' => ['alt' => 'media'],
                    'headers' => [
                        'Range' => sprintf('bytes=%s-%s', $chunkStart, $chunkEnd)
                    ]
                ]
            );
            $chunkStart = $chunkEnd + 1;
            fwrite($fp, $response->getBody()->getContents());
        }

        // close the file pointer
        fclose($fp);

        echo "File is stored to your filesystem.";
    } catch(Exception $e) {
        if( 401 == $e->getCode() ) {
            $refresh_token = $db->get_refersh_token();
 
            $client = new GuzzleHttp\Client(['base_uri' => 'https://accounts.google.com']);
 
            $response = $client->request('POST', '/o/oauth2/token', [
                'form_params' => [
                    "grant_type" => "refresh_token",
                    "refresh_token" => $refresh_token,
                    "client_id" => GOOGLE_CLIENT_ID,
                    "client_secret" => GOOGLE_CLIENT_SECRET,
                ],
            ]);
 
            $data = (array) json_decode($response->getBody());
            $data['refresh_token'] = $refresh_token;
 
            $db->update_access_token(json_encode($data));
 
            download_drive_file($filename);
        } else {
            echo $e->getMessage(); //print the error
        }
    }
}

Delete File from Google Drive

In some scenarios, you may want to delete the file from Google Drive. We can delete the file by using the following code.

<?php
require_once 'config.php';

delete_file_from_drive('FILE_NAME_HERE'); // say 1.jpg
 
function delete_file_from_drive($filename = '') {
 
    $client = new Google_Client();
 
    $db = new DB();
 
    $arr_token = (array) $db->get_access_token();
    $accessToken = array(
        'access_token' => $arr_token['access_token'],
        'expires_in' => $arr_token['expires_in'],
    );
 
    $client->setAccessToken($accessToken);

    $service = new Google\Service\Drive($client);

    try {
        // Check for $filename and include the file ID and size
        $files = $service->files->listFiles([
            'q' => "name='$filename'",
            'fields' => 'files(id)'
        ]);

        if(count($files) == 0) {
            echo "No files found.";
            return;
        }

        // Determine the file's ID
        $fileId = $files[0]->id;

        $result = $service->files->delete($fileId);

        echo "File is deleted from Google Drive.";
    } catch(Exception $e) {
        if( 401 == $e->getCode() ) {
            $refresh_token = $db->get_refersh_token();
 
            $client = new GuzzleHttp\Client(['base_uri' => 'https://accounts.google.com']);
 
            $response = $client->request('POST', '/o/oauth2/token', [
                'form_params' => [
                    "grant_type" => "refresh_token",
                    "refresh_token" => $refresh_token,
                    "client_id" => GOOGLE_CLIENT_ID,
                    "client_secret" => GOOGLE_CLIENT_SECRET,
                ],
            ]);
 
            $data = (array) json_decode($response->getBody());
            $data['refresh_token'] = $refresh_token;
 
            $db->update_access_token(json_encode($data));
 
            delete_file_from_drive($filename);
        } else {
            echo $e->getMessage(); //print the error
        }
    }
}

Conclusion

In this tutorial, I explained how to integrate Google Drive API with PHP. We wrote a code to perform operations like creating a folder, uploading a file, downloading a file, and deleting a file on a Drive. I hope this article will help you to incorporate Drive API into your application. I would like to hear your thoughts and suggestions in the comment section below.

Related Articles

If you liked this article, then please subscribe to our YouTube Channel for video tutorials.

11 thoughts on “How to Integrate Google Drive API with PHP – Upload/Download/Delete File on Drive

  1. Thank you so much for this great article. I hope this message finds you well. I am reaching out to seek your insights and advice regarding the Google Drive OAuth verification process. I’ve been working on a project that involves integrating Google Drive functionalities into our application, and I’m currently in the process of preparing for OAuth verification.

    I understand that you have experience with this process, and I would greatly appreciate your guidance on the following aspects:

    Preparation: What steps did you take to prepare for the Google Drive OAuth verification?

    Expectations: Could you provide some insights into what to expect during the verification process? Were there any unexpected challenges or requirements that you encountered?

    Successfully Verified: If you used a restricted scope during your verification, I’d like to understand how you ensured a successful outcome. Are there any best practices or tips you can share to navigate the verification process successfully when using a restricted scope?

    Your experience and advice would be invaluable to me as I embark on this journey. I want to ensure that our integration with Google Drive is not only secure but also compliant with their OAuth verification standards.

    If you have some time to spare for a brief discussion or are willing to share any relevant documentation or resources, I would be extremely grateful.

    Thank you in advance for your assistance, and once again, for this great blog.

  2. Hello,
    Thanks for the detailed article!

    When I tried to run create-folder.php it returned the following error:

    Fatal error: Uncaught TypeError: json_decode(): Argument #1 ($json) must be of type string, array given in C:\xampp\htdocs\server\vendor\google\apiclient\src\Google\Auth\OAuth2.php :184 Stack trace: #0 C:\xampp\htdocs\server\vendor\google\apiclient\src\Google\Auth\OAuth2.php(184): json_decode(Array, true) #1 C:\xampp\htdocs\ server\vendor\google\apiclient\src\Google\Client.php(218): Google_Auth_OAuth2->setAccessToken(Array) #2 C:\xampp\htdocs\server\googleDocs\create-folder.php(19): Google_Client-> >setAccessToken(Array) #3 C:\xampp\htdocs\server\googleDocs\create-folder.php(4): create_drive_folder(‘apifolder’) #4 {main} thrown in C:\xampp\htdocs\server\vendor \google\apiclient\src\Google\Auth\OAuth2.php on line 184

    Would appreciate help,
    Thank you!

  3. Hi, first of all thank you for all the info! A minor suggestion would be that spellingwise the function that extracts a refresh token from the column’s value should be named “get_refresh_token()” and not as is the case: “get_refersh_token()”. Executionwise it does not matter of course as long as all calls are made with the name given, which they are.

  4. Why does your composer set to install api v2? Google has had api v3 for many years. So, why install the old version?!

  5. is there a way you can share a folder in a specific email? where that user email can upload file in the folder?

  6. Oops!
    I have reviewed the code for a while, yeah, workflow is correct, but callback.php is not calling.
    Can you help me why it’s not working?
    Thank you.

  7. Hi thanks for sharing great blog.
    But when I run create-file.php(I created create-file.php file for creating a new folder in google drive), I get this error.

    http://prntscr.com/wyvWV5h0ANMC

    I think, it’s because table is empty yet for the first time and trying to get data from empty table.
    You didn’t mention about it.
    I hope you share it as well.
    I use google drive api first time, so I don’t know well.
    Please guide me.
    Thank you.

Leave a Reply

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