[Tut] How to Upload Files to Google Drive with API using PHP - Printable Version +- Sick Gaming (https://www.sickgaming.net) +-- Forum: Programming (https://www.sickgaming.net/forum-76.html) +--- Forum: PHP Development (https://www.sickgaming.net/forum-82.html) +--- Thread: [Tut] How to Upload Files to Google Drive with API using PHP (/thread-99769.html) |
[Tut] How to Upload Files to Google Drive with API using PHP - xSicKxBot - 08-04-2022 How to Upload Files to Google Drive with API using PHP <div style="margin: 5px 5% 10px 5%;"><img src="https://www.sickgaming.net/blog/wp-content/uploads/2022/08/how-to-upload-files-to-google-drive-with-api-using-php.jpg" width="550" height="467" title="" alt="" /></div><div><div class="modified-on" readability="7.0909090909091"> by <a href="https://phppot.com/about/">Vincy</a>. Last modified on August 5th, 2022.</div> <p>Uploading files to Google Drive programmatically can be done by the Google API. It uses <a href="https://phppot.com/php/php-google-oauth-login/">OAuth to authenticate</a> requests and authorize access.</p> <p>This tutorial describes uploading files to Google Drive using PHP. It gives a simple PHP script to easily understand the Google API and upload files.</p> <p>It also uses a database to save the uploaded file details with the Google Drive reference.</p> <p>It handles errors that can occur for the following reasons during the upload process.</p> <ol> <li>When the file binary is empty on the PHP script.</li> <li>When the user fails to submit the form and proceeds to upload without form data.</li> <li>When the Google OAuth request is failed to get the access token.</li> <li>When the cURL request to the Google API is failed to return the status code 200.</li> </ol> <p>On successful upload without any of the above uncertainties, this code shows the Google Drive link to see the uploaded file preview.</p> <p>The below figure shows the file upload form with the success and failure responses.</p> <p><img loading="lazy" class="alignnone size-large wp-image-18801" src="https://phppot.com/wp-content/uploads/2022/08/php-google-drive-upload-550x467.jpg" alt="php google drive upload" width="550" height="467" srcset="https://phppot.com/wp-content/uploads/2022/08/php-google-drive-upload-550x467.jpg 550w, https://phppot.com/wp-content/uploads/2022/08/php-google-drive-upload-300x255.jpg 300w, https://phppot.com/wp-content/uploads/2022/08/php-google-drive-upload-768x652.jpg 768w, https://phppot.com/wp-content/uploads/2022/08/php-google-drive-upload.jpg 800w" sizes="(max-width: 550px) 100vw, 550px"></p> <h2>How to create API credentials to access Google Drive</h2> <p>Login to your Google account and go to the <a href="https://console.developers.google.com" target="_blank" rel="noopener">developer console</a>. Then follow the below steps to create API credentials to access Google Drive to upload a file.</p> <ol> <li>Create a new project or select an existing project from the Google console header.</li> <li>Click the <em>Library</em> menu and enable <strong>Google Drive API<i>.</i></strong> Use the filter to shortlist this API.</li> <li>Choose the <em>OAuth consent screen</em> menu to create the app. Fill up the following to register the app. <ul> <li>App name</li> <li>support email</li> <li>authorized domain</li> <li>developer contact detail (email).</li> </ul> </li> <li>Select <em>Credentials->Create Credentials</em>, then select <em>OAuth client ID</em>. Then, enter the following details. <ul> <li>Choose <em>Application type</em> as <em>Web Application</em>.</li> <li>Add authorized <a href="https://phppot.com/php/how-to-get-current-location-using-google-map-javascript-api/">JavaScript origin</a>.</li> <li>Add authorized redirect URI.</li> </ul> </li> </ol> <p>After completing these steps, the console will display the Google web client id and the secret key. These credentials are used for the authentication process to get access to Google Drive.</p> <p><img loading="lazy" class="alignnone size-large wp-image-18807" src="https://phppot.com/wp-content/uploads/2022/08/oauth-credential-550x546.jpg" alt="oauth credential" width="550" height="546" srcset="https://phppot.com/wp-content/uploads/2022/08/oauth-credential-550x546.jpg 550w, https://phppot.com/wp-content/uploads/2022/08/oauth-credential-300x298.jpg 300w, https://phppot.com/wp-content/uploads/2022/08/oauth-credential-150x150.jpg 150w, https://phppot.com/wp-content/uploads/2022/08/oauth-credential.jpg 600w" sizes="(max-width: 550px) 100vw, 550px"></p> <h2>Example application files structure</h2> <p>Let us see the PHP example code created for this article to upload a file to Google Drive. The following figure shows the file structure of this example.</p> <p><img loading="lazy" class="alignnone size-full wp-image-18817" src="https://phppot.com/wp-content/uploads/2022/08/google-drive-file-upload-example.jpg" alt="google drive file upload example" width="300" height="366" srcset="https://phppot.com/wp-content/uploads/2022/08/google-drive-file-upload-example.jpg 300w, https://phppot.com/wp-content/uploads/2022/08/google-drive-file-upload-example-246x300.jpg 246w" sizes="(max-width: 300px) 100vw, 300px"></p> <h2>Application config file</h2> <p>This PHP file contains the constants used in this example. The API credentials and the endpoints are stored as <a href="https://phppot.com/php/php-constants/">PHP constants</a> with this file.</p> <p>The endpoint URI configured in this file is to hit the Google Drive API for the following purpose.</p> <ul> <li>To set scope during OAuth redirect.</li> <li>To get the access token after authentication with the API credentials <em><code class="language-php">GOOGLE_WEB_CLIENT_ID</code> and <code class="language-php">GOOGLE_WEB_CLIENT_SECRET</code></em>.</li> <li>To upload file to Drive</li> <li>To add metadata to the uploaded file</li> </ul> <p>The <code class="language-php">AUTHORIZED_REDIRECT_URI</code> is to set the callback. The API will call this URI with the access code to proceed with file upload after <a href="https://phppot.com/php/user-registration-in-php-with-login-form-with-mysql-and-code-download/">authentication</a>.</p> <p class="code-heading">lib/Config.php</p> <pre class="prettyprint"><code class="language-php"><?php class Config { const GOOGLE_WEB_CLIENT_ID = 'add client id'; const GOOGLE_WEB_CLIENT_SECRET = 'add client secret'; const GOOGLE_ACCESS_SCOPE = 'https://www.googleapis.com/auth/drive'; const AUTHORIZED_REDIRECT_URI = 'https://domain-name/php-google-drive-upload/callback.php'; const GOOGLE_OAUTH2_TOKEN_URI = 'https://oauth2.googleapis.com/token'; const GOOGLE_DRIVE_FILE_UPLOAD_URI = 'https://www.googleapis.com/upload/drive/v3/files'; const GOOGLE_DRIVE_FILE_META_URI = 'https://www.googleapis.com/drive/v3/files/'; } ?> </code></pre> <h2>Landing form with file upload option</h2> <p>This is a simple HTML form that calls the PHP endpoint <em>upload.php</em> on submitting. The file data is posted to this PHP file to upload to a local directory and to Google Drive.</p> <p>I have just managed field validation by using HTML5 <em>required</em> attribute. You can also add exclusive<a href="https://phppot.com/jquery/file-size-validation-using-jquery/"> JavaScript validation for this file upload</a> form.</p> <p>We have already seen code for doing <a href="https://phppot.com/php/php-image-upload-with-size-type-dimension-validation/">server-side file validation in PHP</a>.</p> <p class="code-heading">index.php</p> <pre class="prettyprint"><code class="language-php-template"><?php session_start(); ?> <html> <head> <title>How to upload file to Google drive</title> <link rel="stylesheet" type="text/css" href="css/style.css" /> <link rel="stylesheet" type="text/css" href="css/form.css" /> <style> input.btn-submit { background: #ffc72c url("google-drive-icon.png") no-repeat center left 45px; text-align: right; padding-right: 45px; } </style> </head> <body> <div class="phppot-container tile-container"> <form method="post" action="upload.php" class="form" enctype="multipart/form-data"> <?php if(!empty($_SESSION['responseMessage'])){ ?> <div id="phppot-message" class="<?php echo $_SESSION['responseMessage']['messageType']; ?>"> <?php echo $_SESSION['responseMessage']['message']; ?> </div> <?php $_SESSION['responseMessage'] = ""; } ?> <h2 class="text-center">Upload file to drive</h2> <div> <div class="row"> <label class="inline-block">Select file to upload</label> <input type="file" name="file" class="full-width" required> </div> <div class="row"> <input type="submit" name="submit" value="Upload to Drive" class="btn-submit full-width"> </div> </div> </form> </div> </body> </html> </code></pre> <h2>PHP code upload file to a directory, save to database and redirect to Google</h2> <p>This HTML form action endpoint performs file upload to a directory. It <a href="https://phppot.com/php/php-file-upload/">saves the file path to the database</a> and redirects to the Google OAuth URI.</p> <p>This URI sets the scope, app client id and <a href="https://phppot.com/php/php-redirect/">redirect</a> path (callback.php) to get the access code from the Google Drive API endpoint.</p> <p>In case of error occurrence, it calls application utils to acknowledge and guide users properly.</p> <p class="code-heading">upload.php</p> <pre class="prettyprint"><code class="language-php"><?php session_start(); require_once __DIR__ . '/lib/Util.php'; $util = new Util(); if (! empty($_POST['submit'])) { require_once __DIR__ . '/lib/Config.php'; require_once __DIR__ . '/lib/FileModel.php'; $fileModel = new FileModel(); if (! empty($_FILES["file"]["name"])) { $fileName = basename($_FILES["file"]["name"]); $targetFilePath = "data/" . $fileName; if (move_uploaded_file($_FILES["file"]["tmp_name"], $targetFilePath)) { $fileInsertId = $fileModel->insertFile($fileName); if ($fileInsertId) { $_SESSION['fileInsertId'] = $fileInsertId; $googleOAuthURI = 'https://accounts.google.com/o/oauth2/auth?scope=' . urlencode(Config::GOOGLE_ACCESS_SCOPE) . '&redirect_uri=' . Config::AUTHORIZED_REDIRECT_URI . '&response_type=code&client_id=' . Config::GOOGLE_WEB_CLIENT_ID . '&access_type=online'; header("Location: $googleOAuthURI"); exit(); } else { $util->redirect("error", 'Failed to insert into the database.'); } } else { $util->redirect("error", 'Failed to upload file.'); } } else { $util->redirect("error", 'Choose file to upload.'); } } else { $util->redirect("error", 'Failed to find the form data.'); } ?> </code></pre> <h2>Callback action to get access token and proceed file upload to Google Drive</h2> <p>This page is called by Google API after performing the OAuth request. The API sends a <em>code</em> parameter while calling this redirect URL.</p> <p>It calls the <code class="language-php">getAccessToken()</code> a function defined in the service class. It passes API credentials to get the access token.</p> <p>When the token is received, this file builds the file content and file meta to be uploaded to Google Drive via <a href="https://phppot.com/php/php-curl-post/">cURL request</a>.</p> <p>The <code class="language-php">uploadFileToGoogleDrive()</code> accepts access token and the file information to set the cURL options. It returns the file id of the uploaded file to Google Drive.</p> <p>Then, the <code class="language-php">addFileMeta()</code> PHP function accepts the array of file metadata. It returns the Google Drive file meta data received as a cURL response.</p> <p>This metadata id is used in the success response to allow users to <a href="https://phppot.com/jquery/php-image-slideshow-with-jquery-using-multiple-file-upload/">view the uploaded file</a> in Google Drive.</p> <p class="code-heading">callback.php</p> <pre class="prettyprint"><code class="language-php"><?php session_start(); require_once __DIR__ . '/lib/Util.php'; $util = new Util(); if (isset($_GET['code'])) { require_once __DIR__ . '/lib/Config.php'; require_once __DIR__ . '/lib/GoogleDriveUploadService.php'; $googleDriveUploadService = new GoogleDriveUploadService(); $googleResponse = $googleDriveUploadService->getAccessToken(Config::GOOGLE_WEB_CLIENT_ID, Config::AUTHORIZED_REDIRECT_URI, Config::GOOGLE_WEB_CLIENT_SECRET, $_GET['code']); $accessToken = $googleResponse['access_token']; if (! empty($accessToken)) { require_once __DIR__ . '/lib/FileModel.php'; $fileModel = new FileModel(); $fileId = $_SESSION['fileInsertId']; if (! empty($fileId)) { $fileResult = $fileModel->getFileRecordById($fileId); if (! empty($fileResult)) { $fileName = $fileResult[0]['file_base_name']; $filePath = 'data/' . $fileName; $fileContent = file_get_contents($filePath); $fileSize = filesize($filePath); $filetype = mime_content_type($filePath); try { // Move file to Google Drive via cURL $googleDriveFileId = $googleDriveUploadService->uploadFileToGoogleDrive($accessToken, $fileContent, $filetype, $fileSize); if ($googleDriveFileId) { $fileMeta = array( 'name' => basename($fileName) ); // Add file metadata via Google Drive API $googleDriveMeta = $googleDriveUploadService->addFileMeta($accessToken, $googleDriveFileId, $fileMeta); if ($googleDriveMeta) { $fileModel->updateFile($googleDriveFileId, $fileId); $_SESSION['fileInsertId'] = ''; $driveLink = '<a href="https://drive.google.com/open?id=' . $googleDriveMeta['id'] . '" target="_blank"><b>Open in Google Drive</b></a>.'; $util->redirect("success", 'File uploaded. ' . $driveLink); } } } catch (Exception $e) { $util->redirect("error", $e->getMessage()); } } else { $util->redirect("error", 'Failed to get the file content.'); } } else { $util->redirect("error", 'File id not found.'); } } else { $util->redirect("error", 'Something went wrong. Access forbidden.'); } } ?> </code></pre> <h2>PHP service class to prepare requests and hit Google Drive API via cURL</h2> <p>The service class contains functions that build the PHP cURL request to hit the Google Drive API.</p> <p>All the cURL <a href="https://phppot.com/php/php-request-methods/">requests use POST methods</a> to submit parameters to the API endpoints.</p> <p>It gets the response code and the data in the specified format. In case of a cURL error or getting a response code other than 200, it throws exceptions.</p> <p>On getting the status code 200, it receives the Google Drive file reference and metadata JSON response appropriately.</p> <p class="code-heading">lib/GoogleDriveUploadService.php</p> <pre class="prettyprint"><code class="language-php"><?php require_once __DIR__ . '/Config.php'; class GoogleDriveUploadService { public function getAccessToken($clientId, $authorizedRedirectURI, $clientSecret, $code) { $curlPost = 'client_id=' . $clientId . '&redirect_uri=' . $authorizedRedirectURI . '&client_secret=' . $clientSecret . '&code=' . $code . '&grant_type=authorization_code'; $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, Config::GOOGLE_OAUTH2_TOKEN_URI); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curl, CURLOPT_POST, 1); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($curl, CURLOPT_POSTFIELDS, $curlPost); $curlResponse = json_decode(curl_exec($curl), true); $responseCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); if ($responseCode != 200) { $errorMessage = 'Problem in getting access token'; if (curl_errno($curl)) { $errorMessage = curl_error($curl); } throw new Exception('Error: ' . $responseCode . ': ' . $errorMessage); } return $curlResponse; } public function uploadFileToGoogleDrive($accessToken, $fileContent, $filetype, $fileSize) { $curl = curl_init(); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($curl, CURLOPT_URL, Config::GOOGLE_DRIVE_FILE_UPLOAD_URI . '?uploadType=media'); curl_setopt($curl, CURLOPT_BINARYTRANSFER, 1); curl_setopt($curl, CURLOPT_POST, 1); curl_setopt($curl, CURLOPT_POSTFIELDS, $fileContent); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_HTTPHEADER, array( 'Content-Type: ' . $filetype, 'Content-Length: ' . $fileSize, 'Authorization: Bearer ' . $accessToken )); $curlResponse = json_decode(curl_exec($curl), true); $responseCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); if ($responseCode != 200) { $errorMessage = 'Failed to upload file to drive'; if (curl_errno($curl)) { $errorMessage = curl_error($curl); } throw new Exception('Error ' . $responseCode . ': ' . $errorMessage); } curl_close($curl); return $curlResponse['id']; } public function addFileMeta($accessToken, $googleDriveFileId, $fileMeta) { $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, Config::GOOGLE_DRIVE_FILE_META_URI . $googleDriveFileId); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curl, CURLOPT_POST, 1); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($curl, CURLOPT_HTTPHEADER, array( 'Content-Type: application/json', 'Authorization: Bearer ' . $accessToken )); curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'PATCH'); curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($fileMeta)); $curlResponse = json_decode(curl_exec($curl), true); $responseCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); if ($responseCode != 200) { $errorMessage = 'Failed to add file metadata'; if (curl_errno($curl)) { $errorMessage = curl_error($curl); } throw new Exception('Error ' . $responseCode . ': ' . $errorMessage); } curl_close($curl); return $curlResponse; } } ?> </code></pre> <h2>PHP model class to build queries and parameters to insert, read and update file data log</h2> <p>This PHP model class defines functions to keep track of the database log for the uploaded file.</p> <p>In the callback, it writes the Google Drive file id with the reference of the last inserted id in the session.</p> <p class="code-heading">lib/FileModel.php</p> <pre class="prettyprint"><code class="language-php"><?php require_once __DIR__ . '/DataSource.php'; class FileModel extends DataSource { function insertFile($fileBaseName) { $query = "INSERT INTO google_drive_upload_response_log (file_base_name, create_at) VALUES (?, NOW())"; $paramType = 's'; $paramValue = array( $fileBaseName ); $insertId = $this->insert($query, $paramType, $paramValue); return $insertId; } function getFileRecordById($fileId) { $query = "SELECT * FROM google_drive_upload_response_log WHERE id = ?"; $paramType = 'i'; $paramValue = array( $fileId ); $result = $this->select($query, $paramType, $paramValue); return $result; } function updateFile($googleFileId, $fileId) { $query = "UPDATE google_drive_upload_response_log SET google_file_id=? WHERE id=?"; $paramType = 'si'; $paramValue = array( $googleFileId, $fileId ); $this->update($query, $paramType, $paramValue); } } ?> </code></pre> <p>This file is a simple PHP Util class having only a redirect function as of now.</p> <p>We can enhance this function by adding more utils. For example, it can have <a href="https://phppot.com/php/php-json-encode-and-decode/">JSON encode decode</a> to convert the cURL response into an array.</p> <p class="code-heading">lib/Util.php</p> <pre class="prettyprint"><code class="language-php"><?php class Util { function redirect($type, $message) { $_SESSION['responseMessage'] = array( 'messageType' => $type, 'message' => $message ); header("Location: index.php"); exit(); } } ?> </code></pre> <h2>Installation steps</h2> <p>Before running this example to upload a file to Google Drive, do the following steps. It will let the development environment be ready with the required configurations and resources.</p> <ol> <li>Configure database details with <em>lib/DataSource.php</em>. The source code includes this file.</li> <li>Configure Google API keys with <em>lib/Config.php</em>. Also, provide the domain and subfolder for setting the callback with <i>AUTHORIZED_REDIRECT_URI</i>.</li> <li>Import the below SQL script into your target database.</li> </ol> <p class="code-heading">sql/structure.sql</p> <pre class="prettyprint"><code class="language-sql">-- -- Table structure for table `google_drive_upload_response_log` -- CREATE TABLE `google_drive_upload_response_log` ( `id` int NOT NULL, `google_file_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `file_base_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `create_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -- -- Indexes for dumped tables -- -- -- Indexes for table `google_drive_upload_response_log` -- ALTER TABLE `google_drive_upload_response_log` ADD PRIMARY KEY (`id`); -- -- AUTO_INCREMENT for dumped tables -- -- -- AUTO_INCREMENT for table `google_drive_upload_response_log` -- ALTER TABLE `google_drive_upload_response_log` MODIFY `id` int NOT NULL AUTO_INCREMENT; </code></pre> <p><a class="download" href="https://phppot.com/downloads/php-google-drive-upload.zip">Download</a></p> <p> <!-- #comments --> </p> <div class="related-articles"> <h2>Popular Articles</h2> </p></div> <p> <a href="https://phppot.com/php/how-to-upload-files-to-google-drive-with-api-using-php/#top" class="top">↑ Back to Top</a> </p> </div> https://www.sickgaming.net/blog/2022/08/04/how-to-upload-files-to-google-drive-with-api-using-php/ |