Search Here

Integrate PHP Google Drive API into your Saas Application with User Login

Integrate PHP Google Drive API into your Saas Application with User Login

In this post, you’ll learn to integrate Google Drive into your Saas Application, and also you can have any user login into their Google Account and access the information.

Features

Below is the list of functionalities users can perform.

  • Any user with Google Account can login.
  • List all Folders/Files
  • Search through specific folders by selecting them.
  • Search folders and files inside the root, collection, trash, and also filter with specific extensions.
  • Download Files using PHP file writing and Web Content Link
  • Order list by name in ascending or descending order.
  • Control max results to be displayed.
  • Paginate the next results.

Table of Contents

Project Structure

Here is your project directory structure.

Your-Project
    | - drive-views // Contains classes and views for listing files and folders
    |
    |    | - classes
    |        | - class.DriveFileManager.php // Custom Class performs retriving files/folders, ordering, download files, create and remove permissions 
    |    | - downloads // Files downloaded from google drive are stored here
    |
    |    | - includes 
    |        | - css_header.php // menus link
    |
    |    | - list.php
    |        
    | - vendor // packages
    | - backend.php // Redirects user to Google Drive Login Page
    | - callback.php // Gets the verification code after successfull login and creates token.json file
    | - class.DriveAuth.php // Manages Authentication 
    | - client_secret.json // Contains client secret key
    | - composer.json 
    | - composer.lock
    | - functions.php // user-defined helper functions
    | - index.php // First page to load 
    | - token.json // contains access token and refresh token

Source Code

In index.php: This Page has a link that redirects to Google Drive Login.

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Google Drive Saas App</title>
</head>
<body>

	<h2>PHP | My Google Drive Login Page</h2>
	
	<a href="backend.php?login=google_drive">Login to Google Drive</a>

	<?php
		if( !empty( $_GET['msg'] ) ){
			echo "<p><strong>" . $_GET['msg'] . "</strong></p>";
		}

		if( !empty( $_GET['logout'] ) && $_GET['logout'] == "true" ){
			unlink('token.json');
		}
	?>
</body>
</html>

In functions.php: This Page contains helper functions.

<?php

// These functions are used for easier debugging
function dd( ...$args ){
    echo '<pre style="background:#000;color:#fff;" >';
    print_r( $args );
    exit; 
}

function dump( ...$args ){
    echo '<pre style="background:#000;color:#fff;" >';
    print_r( $args );
}

function redirect( $path ){
    header('Location:'.$path);
    exit;
}

In backend.php: When a user clicks on the login link below code is executed which returns google drive login URL.

<?php
require __DIR__ . '/class.DriveAuth.php';
    
if( isset( $_GET['login'] ) && $_GET['login'] == "google_drive" ){
    // authenticate user
    $client = new DriveAuth();

    $url = $client->getRequestAuthorizationUrl();
    
    if( $url ){
        return redirect( $url );
    } else {
        echo "Unable to redirect to google drive. Please try again by going to <a href='index.php'>Login Page</a>";
    }

}

?>

In callback.php: After successful login, the verification code from Drive API is retrieved by the method processAuthCode().And it creates token.json the file.

<?php

require_once __DIR__ . '/class.DriveAuth.php';
require_once __DIR__ . '/functions.php';
    
// This is a callback page
if( isset( $_GET['code'] ) && !empty( $_GET['code'] ) ){
    $client = new DriveAuth();

    $is_authorized = $client->processAuthCode( $_GET['code'] );

    if( $is_authorized ){
        return redirect('drive-views/list.php');
    } else {
        redirect('index.php?msg=Failed to authorized. Please wait again...');
    }
}

In class.DriveAuth.php: This custom class will handle all the authentication and token refreshing procedures.

<?php
error_reporting(E_ERROR | E_PARSE);
require __DIR__ . '/vendor/autoload.php';

require __DIR__ . '/functions.php';

define( 'project_root', 'C:/xampp/htdocs/test-examples/php/google-drive-saas-app/' );

class DriveAuth{

    protected $google_client;
    protected $token_path = project_root.'token.json';
    protected $verification_code_path = 'verification-code.json';

    public function __construct()
    {
        $this->setClient(); 
    }

    public function getClient(){
        return $this->google_client;
    }

    public function setClient(){
        $this->google_client = new Google_Client();
        $this->google_client->setApplicationName('Google Drive API PHP Quickstart');
        $this->google_client->setScopes(Google_Service_Drive::DRIVE); // make it DRIVE
        $this->google_client->setAuthConfig( project_root.'client_secret.json'); //change this to the name of file you have saved
        $this->google_client->setAccessType('offline');
        $this->google_client->setPrompt('select_account consent');
    }

    public function checkTokenExists(){  
        try {
            $is_token_exists = false;
            // check if file exists
            // after success you will see token.json file into your project
            if (file_exists( $this->token_path )) {
                $accessToken = json_decode(file_get_contents( $this->token_path ), true);
                $this->google_client->setAccessToken($accessToken);

                if ( !$this->google_client->isAccessTokenExpired()) {
                    $is_token_exists = true;
                }
            } else {
                echo "It looks like You'r not logged in <a href='../index.php'>Login Page</a>";
                exit;
            }
        } catch (\Exception $e) {
            $is_token_exists = false;
        }

        return $is_token_exists;
    }

    public function generateNewTokenViaRefreshToken(){
        try {
            $is_success = false;
            // if token does not exists
            if( !$this->checkTokenExists() ){
                // Refresh the token if possible, else fetch a new one.
                if ( $this->google_client->getRefreshToken()) {
                    $this->google_client->fetchAccessTokenWithRefreshToken( $this->google_client->getRefreshToken() );
                    $is_success = true;
                }
            }

        } catch (\Exception $e) {
            $is_success = false;
        }

        return $is_success;
    }

    public function getRequestAuthorizationUrl(){
        try {
            $url = $this->google_client->createAuthUrl();
        } catch (\Exception $e) {
            $url = null;
        }

        return $url;
    }

    public function processAuthCode( $code ){
        try {
            
            $is_authorized = false;

            $code = trim($code);
            $access_token = $this->google_client->fetchAccessTokenWithAuthCode( $code );
            $this->google_client->setAccessToken($access_token);

            // Check to see if there was an error.
            if (array_key_exists('error', $access_token)) {
                throw new Exception(join(', ', $access_token));
            }

            // Save the token to a file and set permissions.
            if (!file_exists(dirname( $this->token_path ))) {
                mkdir(dirname( $this->token_path ), 0700, true);
            }
            file_put_contents( $this->token_path, json_encode( $this->google_client->getAccessToken() ) );

            $is_authorized = true;
        } catch (\Exception $e) {
            $is_authorized = false;
        }

        return $is_authorized;
    }

    public function getServiceDrive(){
        return ( new Google_Service_Drive( $this->google_client ) );
    }

    public function getDriveFile(){
        return ( new Google_Service_Drive_DriveFile( ) );
    }

}

In drive-views/classes/class.DriveFileManager.php: This class works will files/folders and can filter, query fields, create, or removed permissions associated with any file.

<?php

require_once '../class.DriveAuth.php';

class DriveFileManager{

    protected $google_client = null;
    
    
    public function __construct()
    {
        $this->google_client = new DriveAuth();   
    }
    
    public function get(  ){ 
        $this->google_client->checkTokenExists();

        $service = $this->google_client->getServiceDrive();

        $files = $this->google_client->getDriveFile();

        
        $parameters['pageSize'] = ( !empty( $_GET['max_results'] ) )? $_GET['max_results'] : 10;
        $parameters['fields'] = 'files(id, name, modifiedTime, mimeType, parents, trashed, permissions, webViewLink, webContentLink, exportLinks), nextPageToken'; 

        if( !empty( $_GET['nextPageToken'] ) ){
            $parameters['pageToken'] = $_GET['nextPageToken'];
        }

        if( isset( $_GET['advance-search-submit-button'] ) ){
            $query = $this->generateQuery(); 
            $parameters = array_merge( $query,  $parameters );
        }
        
        if( !empty( $_GET['orderBy'] ) ){
            $str = $_GET['orderBy'];

            if( !empty( $_GET['sort_type'] ) ){
                $str .= " ".$_GET['sort_type'];
            }

            $parameters['orderBy']= $str;
        }

        $file_list = $service->files->listFiles($parameters);

        $nextPageToken = '';
        if( !empty( $file_list->nextPageToken ) ){
            $nextPageToken = $file_list->nextPageToken;
        }

        $list = [];

        if( !empty( $file_list ) ){
            foreach( $file_list as $k => $file ){
                array_push( $list, $file );
            }
        }

        return [
            'nextPageToken' => $nextPageToken,
            'list' => $list,
        ];
    }

    public function orderBy(  ){
        $get = $_GET;
        $query = [];


        if( !empty( $get['orderBy'] ) ){
            $str = $get['orderBy'];

            if( !empty( $get['sort_type'] ) ){
                $str .= " ".$get['sort_type'];
            }

            $query['orderBy']= $str;
        }

        return $query;
    }

    public function generateQuery(  ){
        $get = $_GET;
        $query = [];

        if( !empty( $get['search'] ) ){
            $query['q'][] = " name contains '". $get['search'] ."' ";
        }

        if( !empty( $get['search_in_parent_folder'] ) ){
            $query['q'][] = " 'root' in parents ";
        }

        if( !empty( $get['show_from_trashed'] ) ){
            $query['q'][] = " trashed=true ";
        } 

        if( !empty( $get['exclude_from_trashed'] ) ){
            $query['q'][] = " trashed=false ";
        } 

        if( !empty( $get['selected-files-folders'] ) ){
            $array_string = [];
            foreach( $get['selected-files-folders'] as $k => $folder_id ){
                $array_string[] = " '". $folder_id ."' in parents ";
            }
            $array_strings = implode(" or ", $array_string );
            $query['q'][] = " ( " . $array_strings . " ) ";
        }
        
        if( !empty( $get['file_extensions'] ) ){
            
            $mime_types = explode( ',', $get['file_extensions'] );

            $array_strings = [];
            
            if( !empty( $mime_types ) ){
                foreach( $mime_types as $k => $mime_type ){
                    $array_strings[] = " mimeType='".$mime_type."' ";
                }
                $array_strings = implode(" or ", $array_strings );

                $query['q'][] = " ( " . $array_strings . " ) ";
            }

        }

        if( !empty( $query['q'] ) ){
            $query['q'] = implode(" and ", $query['q'] );
        }

        return $query;
    }

    public function downloadFile( $file_id, $name ){
        try {
            $this->google_client->checkTokenExists();

            $service = $this->google_client->getServiceDrive();

            $info = $service->files->get( $file_id, array("alt" => "media") );
        
            $path = project_root.'drive-views/downloads/'.$name;
            
            $outHandle = fopen( $path , "a+");

            while ( ! $info->getBody()->eof()) {
                fwrite( $outHandle, $info->getBody()->read(1024) );
            }

            fclose($outHandle);
            
            return redirect('downloads/'.$name);
        } catch (\Exception $th) {
            throw $th;
        }
    }

    public function createPermission(){
        try{
            $this->google_client->checkTokenExists();

            $success = false;

            $service = $this->google_client->getServiceDrive();

            $newPermission = new Google_Service_Drive_Permission();
            $newPermission->setType( $_POST['type'] );
            $newPermission->setRole( $_POST['role'] );
    
            if( $_POST['type'] == "user" ){
                $newPermission->setEmailAddress( $_POST['emailAddress'] );
            }
    
            if( $_POST['type'] == "domain" ){
                $newPermission->setDomain( $_POST['domain'] );
            }

            $file_id = $_POST['file_id'];

            $service->permissions->create( $file_id, $newPermission);

            $success = true;
        } catch (\Exception $th) {
            $success = false;
            throw $th;
        }

        return $success;
    }

    public function removePermission(){
        try{
            $this->google_client->checkTokenExists();

            $success = false;

            $service = $this->google_client->getServiceDrive();

            $file_id = $_GET['file_id'];
            $permission_id = $_GET['remove-permission-id'];

            $service->permissions->delete( $file_id, $permission_id);

            $success = true;
        } catch (\Exception $th) {
            $success = false;
            throw $th;
        }

        return $success;
    }

}

?>

In drive-views/includes/css_header.php

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Google Drive Saas App</title>
	<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
	<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"></script>
</head>

<nav class="navbar bg-primary navbar-dark navbar-expand-sm">
	<ul class="navbar-nav">
	<li class="nav-item">
		<a class="nav-link" style="color:#000;font-weight:bold;" href="#">Manage Files from Google Drive | </a>
	</li>

	<li class="nav-item">
		<a class="nav-link" style="color:#000;" href="../index.php">Login Page</a>
	</li>

	<li class="nav-item">
		<a class="nav-link" style="color:#000;" href="../index.php?logout=true">Logout</a>
	</li>
	</ul>
</nav>

In list.php

<?php 
error_reporting(E_ALL);
ini_set('display_errors', true);

include_once __DIR__ . '/includes/css_header.php'; 

require_once 'classes/class.DriveFileManager.php'; 

$DriveFileManager = new DriveFileManager();

if( isset( $_POST['create-permission-submit'] ) ){

    $DriveFileManager->createPermission( );
    return redirect('list.php');
}

if( isset( $_GET['remove-permission-id'] ) ){

    $DriveFileManager->removePermission( );
    return redirect('list.php');
}

// download File
if( isset( $_GET['download-file-id'] ) && !empty( $_GET['download-file-id'] ) ){
    $DriveFileManager->downloadFile( $_GET['download-file-id'], $_GET['name'] );
}

// Advance Search 
[
    'nextPageToken' => $nextPageToken,
    'list' => $list,
] = $DriveFileManager->get();

$query_array = [];
foreach( $_GET as $k => $v ){

    if( ! is_array( $v ) ){
        $query_array[] = $k.'='.$v;
    } else {
        $sub_query_array = [];
        foreach( $v as $kk => $vv ){
            $sub_query_array[] = $k."[" . $kk . "]=".$vv;
        }
        $query_array[] = implode( '&', $sub_query_array );
    }
}

$query_string = implode( '&', $query_array )."&";

// order and sort
$sort_type = ( !empty( $_GET['sort_type'] ) && $_GET['sort_type'] == "desc" )? "asc" : "desc";

?>

<body>
    <form action="" method="get" >
        <div class="container-fluid">
            <h2>My Drive Files and Folders</h2>

            <ul class="nav nav-tabs" id="myTab" role="tablist">
                <li class="nav-item">
                    <a class="nav-link active" id="advance Search-tab" data-toggle="tab" href="#advance Search" role="tab" aria-controls="advance Search" aria-selected="true">Advance Search</a>
                </li>
            </ul>

            <div class="tab-content" id="myTabContent">
                <div class="tab-pane fade show active" id="advance Search-tab" role="tabpanel" aria-labelledby="advance Search-tab">
                    <div class="row">
                        <div class="col-md-4">
                            <label for="">Search</label>
                            <input type="search" class="form-control" name="search" value="<?php echo (!empty( $_GET['search'] ) )? $_GET['search'] : ''; ?>" >
                            <p><small>You can also search inside a specific folder by selecting checkboxes.</small></p>
                        </div>

                        <div class="col-md-4">
                            <label for="">File Extensions</label>
                            <input type="file_extensions" class="form-control" name="file_extensions" value="<?php echo (!empty( $_GET['file_extensions'] ) )? $_GET['file_extensions'] : ''; ?>" >
                            <p><small>You can also search multiple files extension separated by comma.</small></p>
                        </div>                        
                    </div>

                    <div class="row" style="margin-top: 2px;" >
                        <div class="col-md-4" >
                            <label for="search_in_parent_folder"><input type="checkbox" name="search_in_parent_folder" value="1" <?php echo (!empty( $_GET['search_in_parent_folder'] ) )? 'checked="checked"' : ''; ?> > Search Only in Root/Parent Folder</label>
                        </div>

                        <div class="col-md-4" >
                            <label for="show_from_trashed"><input type="checkbox" name="show_from_trashed" value="1" <?php echo (!empty( $_GET['show_from_trashed'] ) )? 'checked="checked"' : ''; ?> > Include search results from Trashed also</label>
                        </div>

                        <div class="col-md-4" >
                            <label for="exclude_from_trashed"><input type="checkbox" name="exclude_from_trashed" value="1" <?php echo (!empty( $_GET['exclude_from_trashed'] ) )? 'checked="checked"' : ''; ?> > Exclude trashed items from search result.</label>
                        </div>
                    </div>

                    <div class="row">
                        <div class="col-md-4" style="margin-top: 32px;" >
                            <button type="submit" name="advance-search-submit-button" class="btn btn-info" >Advance Search</button>
                            <a href="list.php" class="btn btn-warning" >Reset Search results</a>
                        </div>
                    </div>
                </div>
            </div>

            <hr>

            <div class="row">
                <div class="col-md-3" >
                    <label for="">Max Results</label>
                    <input type="number" min="1" max="1000" name="max_results" value="<?php echo ( !empty( $_GET['max_results'] ) )? $_GET['max_results'] : 10; ?>" >
                </div>
            </div>
            
            <table class="table table-bordered">
                <thead>
                    <tr>
                    <th> <input type="checkbox" value="" > </th>
                    <th><a href="list.php?<?php echo $query_string; ?>&orderBy=name&sort_type=<?php echo ( !empty( $sort_type ) )? $sort_type : 'ASC'; ?>">Files/Folders ( <?php echo ucfirst($sort_type); ?> ) </a></th>
                    <th>Mime Type</th>
                    <th>Permissions</th>
                    </tr>
                </thead>
                <tbody>
                    <?php if( !empty( $list ) ){ ?>
                        <?php foreach( $list as $k => $item ){ //dd($item); ?>
                        <tr>
                            <td> 
                                <input type="checkbox" name="selected-files-folders[]" value="<?php echo $item['id']; ?>" > 

                                <?php foreach( $item['parents'] as $kk => $parent ){ ?>
                                    <input type="hidden" name="parent_ids[]" value="<?php echo $parent; ?>" > 
                                <?php } ?>
                            </td>
                            <td>
                                <p style="margin:0;" ><?php echo $item['name'] ?> </p>

                                <p style="margin:0;" >
                                <?php if( $item['trashed'] ){ ?>
                                    <span class="badge badge-danger">Trashed</span>
                                    <?php } ?>
                                    <a href="list.php?download-file-id=<?php echo $item['id']; ?>&name=<?php echo $item['name']; ?>" target="_blank" class="badge badge-success">Download</a>
                                    <a href="<?php echo $item['webContentLink']; ?>" target="_blank" class="badge badge-primary">Download via webContentLink</a>
                                </p>
                            </td>
                            <td><?php echo $item['mimeType'] ?></td>
                            <td>
                                <button type="button" onclick="create_permission_modal( '<?php echo $item['id']; ?>', '<?php echo $item['name']; ?>' )" class="btn btn-success btn-sm" > Create New Permission </button>
                                <br>
                                <br>
                                <?php if( !empty( $item->getPermissions( ) ) ){ ?>
                                    <table border="1"  style="border-collapse: collapse;" >
                                        <thead>
                                            <tr>
                                                <th>Type</th>
                                                <th>Role</th>
                                                <th>Display Name</th>
                                                <th>Email Address</th>
                                                <th>Domain</th>
                                                <th>Action</th>
                                            </tr>
                                        </thead>
                                        <tbody>
                                            <?php foreach( $item->getPermissions( ) as $permission ){ ?>
                                                <tr>
                                                    <td> <?php echo $permission['type']; ?> </td>
                                                    <td> <?php echo $permission['role']; ?> </td>
                                                    <td> <?php echo ( !empty( $permission['displayName'] ) )? $permission['displayName'] : '-'; ?> </td>
                                                    <td> <?php echo ( !empty( $permission['emailAddress'] ) )? $permission['emailAddress'] : '-'; ?> </td>
                                                    <td> <?php echo ( !empty( $permission['domain'] ) )? $permission['domain'] : '-'; ?> </td>
                                                    <td> 
                                                        <a href="list.php?remove-permission-id=<?php echo $permission['id'] ?>&file_id=<?php echo $item['id']; ?>" class="btn btn-danger btn-sm"  >Remove</a>
                                                    </td>
                                                </tr>
                                            <?php } ?>
                                        </tbody>
                                    </table>
                                <?php } ?>
                            </td>
                        </tr>
                        <?php } ?>
                    <?php } else { ?> 
                        <tr>
                            <td colspan="3" >No results found.</td>
                        </tr>    
                    <?php } ?>
                </tbody>
            </table>

            <br>
            <ul class="pagination pagination-sm">
                <li class="page-item"><a class="page-link" href="list.php?nextPageToken=<?php echo $nextPageToken; ?>" >Next</a></li>
            </ul>
            
        </div>
    </form>

    <div class="modal fade" id="create_permission_modal">
        <div class="modal-dialog modal-sm">
        <div class="modal-content">        
            <!-- Modal Header -->
            <div class="modal-header">
                <h6 class="modal-title">Create Permission for <span id="permission_for_name" ></span> </h6>
                <button type="button" class="close" data-dismiss="modal">×</button>
            </div>
            
            <!-- Modal body -->
            <div class="modal-body">
                <form action="list.php" method="post">
                    <input type="hidden" name="file_id" id="file_id" value="" >
                    <div class="row">
                        <div class="col-md-12">
                            <label for="">Permission Type</label>
                            <select class="form-control" onchange="permission_type_changed( this )" name="type" id="type" required="required" >
                                <option value="">Choose One</option>
                                <option value="user">User</option>
                                <option value="group">Group</option>
                                <option value="domain">Domain</option>
                                <option value="anyone">Anyone</option>
                            </select>
                        </div>
                    </div>

                    <div class="row">
                        <div class="col-md-12">
                            <label for="">Role</label>
                            <select class="form-control" name="role" id="role" required="required" >
                                <option value="">Choose One</option>
                                <option value="organizer/owner">Organizer/owner</option>
                                <option value="fileOrganizer">FileOrganizer</option>
                                <option value="writer">Writer</option>
                                <option value="commenter">Commenter</option>
                                <option value="reader">Reader</option>
                            </select>
                        </div>
                    </div>

                    <div class="row">
                        <div class="col-md-12" id="template-div" >
                            
                        </div>
                    </div>

                    <div class="row" style="margin-top: 15px;" >
                        <div class="col-md-12"  >
                            <button type="submit" name="create-permission-submit" class="btn btn-info" >Create Permission</button>
                        </div>
                    </div>
                </form>
            </div>
            
            <!-- Modal footer -->
            <div class="modal-footer">
            <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
            </div>
            
        </div>
        </div>
    </div>

    <script>
        let create_permission_modal_element = document.getElementById('create_permission_modal');
        function create_permission_modal( file_id, name ){
            create_permission_modal_element.querySelector('#file_id').value = file_id;
            create_permission_modal_element.querySelector('#permission_for_name').innerText = name;
            $('#create_permission_modal').modal('show');
        }

        function permission_type_changed( element ){
            let ctx = ``;
            if( element.value == "user" ){
                ctx = `
                    <label for="">Email Address</label>  
                    <input type="email" name="emailAddress" class="form-control" value="" required="required" >
                `;
            } 

            if( element.value == "domain" ){
                ctx = `
                    <label for="">Domain name</label>  
                    <input type="text" name="domain" class="form-control" value="" required="required" >
                `;
            } 

            create_permission_modal_element.querySelector('#template-div').innerHTML = ctx;
        }
    </script>

</body>
</html>

Output

Final Output in Integrating PHP Google Drive API into your Saas Application with User Login

Final Output in Integrating PHP Google Drive API into your Saas Application with User Login

Useful Links

For official docs visit Google Drive PHP API
To learn about uploading files visit our post on Google Drive File Upload API using PHP

Udemy PHP Course

Udemy PHP Course

Conclusion

In conclusion, you have reached the end of this post PHP How to Integrate Google Drive into your Saas Application. For more information or suggestions comment below.

Summary
Review Date
Reviewed Item
Integrate PHP Google Drive API into your Saas Application with User Login
Author Rating
51star1star1star1star1star
Software Name
PHP Google Drive API
Software Name
Windows Os, Mac Os and Ubuntu Os
Software Category
PHP API Development