Reputation: 2610
I have a mobile site m.example.com - from a phone I want visitors to choose a file from Google Drive, and send it to the server that hosts m.example.com. Essentially emulating a simple <input type="file">
file upload as on a desktop.
From what I understand the workflow is as follows:
1) User picks file with Picker which sends meta data of the chosen file to my website client (i.e. the HTML/Javascript running on the phone/tablet)
2) I send that to my server via ajax or just a form hidden field
3) my server makes a request to the Google API to get the file and then stores it in the server's file system
So I need help on:
a) is the above steps correct, and is there any other way to do this, or even a service I can use that will allow my site users to pick their files from one of several cloud storage providors?
a) assuming my steps are correct and this is the only way, I am stuck on the 3) part - server talking to the API.
So far I've ceated the picker as per here - Google picker auth popup is being blocked and got the file URL. I've not done 2) yet, I'm just manually putting the file URL into my downlaod script for now.
I'm using PHP and the file I'll want to downlaod to my server could be public or private, that depends on the end user.
I'm lost in the API docs (as in man pages, not a google doc) and am confused with https://developers.google.com/api-client-library/php/start/get_started (call this API docs)and https://developers.google.com/drive/web/quickstart/quickstart-php (call this Drive docs) - are these two different APIs?
I followed the links from the API docs and installed the client from here : https://github.com/google/google-api-php-client, but when trying "Step 3: Set up the sample" on the Drive docs I get many errors such as files not found, class not fount etc, so that makes me think therr is two different APIs/Clients being documented here - can someone please point me in the right direction to get started?
UPDATE
I've re installed the PHP client vis the github linked from this https://developers.google.com/api-client-library/php/start/get_started
This is that it looks like:
I ran the simplefileupload.php in the examples directory - worked first time, only had to put in my project details
So went to https://developers.google.com/drive/web/quickstart/quickstart-php#step_3_set_up_the_sample and created drive_rest_api_step_3.php in root of google-api (as shown in screen grab)
Got Fatal error: require_once(): Failed opening required 'src/Google_Client.php' (include_path='.:/usr/local/lib/php') in /path/to/google-api/drive_rest_api_step_3.php on line 5
There is no Google_Client.php in the library, but there is src/Google/Client.php so I edit the require_once to use that.
Now get Failed opening required 'src/contrib/Google_DriveService.php'
- again a search for that file yeilds no results, but there is a src/Google/Service/Drive.php, so edit example to use that:
Requires (on https://developers.google.com/drive/web/quickstart/quickstart-php#step_3_set_up_the_sample) was:
require_once 'google-api-php-client/src/Google_Client.php';
require_once 'google-api-php-client/src/contrib/Google_DriveService.php';
Now:
require_once 'src/Google/Client.php';
require_once 'src/Google/Service/Drive.php';
Now getting Fatal error: Class 'Google_Service' not found in /path/to/google-api/src/Google/Service/Drive.php on line 32
So this is why I think there is an issues with the two sets of guides, either they use different libraries, or https://developers.google.com/drive/web/quickstart/quickstart-php#step_3_set_up_the_sample is out of date, even though is says Last updated March 30, 2015.
Upvotes: 1
Views: 4478
Reputation: 381
You are right in that the drive quickstart guide is outdated, it refers to the old version of Google's PHP Client API Library that is on Google Code, rather than the newer one on GitHub. Hence the quickstart guide doesn't work with the PHP Client Library you downloaded. In addition, the quickstart guide code is intended to be executed in PHP command-line mode rather than on the server.
To answer the question, I've put in a few sub-answers:
Try this page instead: https://developers.google.com/api-client-library/php/auth/web-app which has an example showing how to list files on a user's Google Drive using the new PHP library, including the whole OAuth procedure.
Unfortunately, even that is a bit outdated (the include path is outdated and is now deprecated). As such (and for StackOverflow's completeness sake) here's some code. I used a subdirectory drivetest in my web server root; change the URLs as necessary.
Do note that you need to get the client ID for "Web applications" in Google's Developer Console, and download the JSON (replacing the client_secrets.json
in the code as necessary).
drivetest/quickstart.php
<?php
require_once 'google-api-php-client/src/Google/autoload.php';
session_start();
$client = new Google_Client();
$client->setAuthConfigFile('client_secrets.json');
$client->addScope(Google_Service_Drive::DRIVE_METADATA_READONLY);
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
$client->setAccessToken($_SESSION['access_token']);
$drive_service = new Google_Service_Drive($client);
$files_list = $drive_service->files->listFiles(array())->getItems();
echo json_encode($files_list);
} else {
$redirect_uri = 'http://localhost/drivetest/oauth2callback.php';
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
?>
drivetest/oauth2callback.php
<?php
require_once 'google-api-php-client/src/Google/autoload.php';
session_start();
$client = new Google_Client();
$client->setAuthConfigFile('client_secrets.json');
$client->setRedirectUri('http://localhost/drivetest/oauth2callback.php');
$client->addScope(Google_Service_Drive::DRIVE_METADATA_READONLY);
if (! isset($_GET['code'])) {
$auth_url = $client->createAuthUrl();
header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
} else {
$client->authenticate($_GET['code']);
$_SESSION['access_token'] = $client->getAccessToken();
$redirect_uri = 'http://localhost/drivetest/quickstart.php';
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
?>
For your use case, you are probably better off using the Google Picker https://developers.google.com/picker/docs/ which implements the whole browse-and-select-file thing for you, and just gives an ID to the file. A summary of the steps required:
We can do this in two ways (client-side or server-side):
Using this method, steps 1 - 3 are done in Javascript, and only step 4 is done in PHP. With this method we don't even need the PHP client library!
Here's an example (adapted from sample code in the above link and http://webdevrefinery.com/forums/topic/12931-dropbox-google-drive-file-pickers/):
picker.html
This file launches the filepicker upon page load and puts the URL into a form.
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title>Google Picker Example</title>
<script type="text/javascript">
// The Browser API key obtained from the Google Developers Console.
var developerKey = '';
// The Client ID obtained from the Google Developers Console. Replace with your own Client ID.
var clientId = ""
// Replace with your own App ID. (Its the first number in your Client ID)
var appId = ""
// Scope to use to access user's Drive items.
var scope = ['https://www.googleapis.com/auth/drive'];
var pickerApiLoaded = false;
var oauthToken;
// Use the Google API Loader script to load the google.picker script.
function loadPicker() {
gapi.load('auth', {'callback': onAuthApiLoad});
gapi.load('picker', {'callback': onPickerApiLoad});
}
function onAuthApiLoad() {
window.gapi.auth.authorize(
{
'client_id': clientId,
'scope': scope,
'immediate': false
},
handleAuthResult);
}
function onPickerApiLoad() {
pickerApiLoaded = true;
createPicker();
}
function handleAuthResult(authResult) {
if (authResult && !authResult.error) {
oauthToken = authResult.access_token;
createPicker();
}
}
// Create and render a Picker object
function createPicker() {
if (pickerApiLoaded && oauthToken) {
var view = new google.picker.DocsView();
view.setIncludeFolders(true);
//view.setMimeTypes("image/png,image/jpeg,image/jpg");
var picker = new google.picker.PickerBuilder()
//.enableFeature(google.picker.Feature.NAV_HIDDEN)
//.enableFeature(google.picker.Feature.MULTISELECT_ENABLED)
.setAppId(appId)
.setOAuthToken(oauthToken)
.addView(view)
.setDeveloperKey(developerKey)
.setCallback(pickerCallback)
.build();
picker.setVisible(true);
}
}
// A simple callback implementation.
function pickerCallback(data) {
if (data.action == google.picker.Action.PICKED) {
var fileId = data.docs[0].id;
gapi.client.load('drive', 'v2', function() {
var request = gapi.client.drive.files.get({
fileId: fileId
});
request.execute(processFile);
});
}
}
function processFile(file) {
var token = gapi.auth.getToken();
// console.log(file);
// console.log(token);
document.getElementById("fileurl").value = file.downloadUrl+"&access_token="+token.access_token;
}
</script>
</head>
<body>
<form action="submit.php" method="post">
<label for="fileurl">File Download URL</label><input type="text" name="fileurl" id="fileurl">
<input type="submit">
</form>
<!-- The Google API Loader script. -->
<script type="text/javascript" src="https://apis.google.com/js/api.js?onload=loadPicker"></script>
<script type="text/javascript" src="https://apis.google.com/js/client.js"></script>
</body>
</html>
We then submit the form to a PHP script to download the file on the server. The trick here is that we also need to pass the access token from the client to the server, since the user is not authenticated on the server side. Surprisingly, you can simply append the access_token
parameter to authenticate the download of the file, as shown above.
submit.php
Use file_get_contents
or CURL, depending on what your server supports. HTTPS support is required for this to work though.
<?php
$filename = 'temp.jpg';
$ch = curl_init($_POST['fileurl']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// Should verify in production!
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$data = curl_exec($ch);
//echo 'Curl error: ' . curl_error($ch);
curl_close($ch);
file_put_contents($filename, $data);
?>
A more official way (following https://developers.google.com/drive/web/manage-downloads#alternate_method_using_downloadurl) is to send the authorization token separately using the Authorization
header. Modify the Javascript above to send the download URL and token separately, then use something like the code below instead. If you want to use file_get_contents
, see PHP file_get_contents() and headers on how to send custom headers. Note that you need to have the Bearer
word before the token!
<?php
$filename = 'temp.jpg';
$ch = curl_init($_POST['fileurl']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Bearer '.$_POST['authtoken']));
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$data = curl_exec($ch);
echo 'Curl error: ' . curl_error($ch);
curl_close($ch);
file_put_contents($filename, $data);
?>
Using this method, steps 1, 3 and 4 are done in PHP, and only step 2 is done in Javascript.
quickstart.php
This page checks if there is an access token in the session, if there is not it redirects the user for authentication. If there is, it shows the picker and a form. In the picker Javascript code, note that the oAuthToken used is obtained with PHP from the server! Source: Use Google Picker without logging in with Google account (with OAuth). The form then submits a POST request to this page and the file is downloaded.
<?php
require_once 'google-api-php-client/src/Google/autoload.php';
session_start();
// Ref: https://developers.google.com/drive/v2/reference/files/get
function downloadFile($service, $file) {
$downloadUrl = $file->getDownloadUrl();
if ($downloadUrl) {
$request = new Google_Http_Request($downloadUrl, 'GET', null, null);
$httpRequest = $service->getClient()->getAuth()->authenticatedRequest($request);
if ($httpRequest->getResponseHttpCode() == 200) {
return $httpRequest->getResponseBody();
} else {
// An error occurred.
return null;
}
} else {
// The file doesn't have any content stored on Drive.
return null;
}
}
$client = new Google_Client();
$client->setAuthConfigFile('client_secrets.json');
$client->addScope(Google_Service_Drive::DRIVE_READONLY);
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
$client->setAccessToken($_SESSION['access_token']);
if (isset($_POST['fileid'])){
$drive_service = new Google_Service_Drive($client);
$file = $drive_service->files->get($_POST['fileid']);
$data = downloadFile($drive_service, $file);
file_put_contents('temp.jpg', $data);
echo "file uploaded";
exit();
}
} else {
$redirect_uri = 'http://localhost/drivepicker-php/oauth2callback.php';
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
exit();
}
?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title>Google Picker Example</title>
<script type="text/javascript">
// The Browser API key obtained from the Google Developers Console.
var developerKey = '';
// Replace with your own App ID. (Its the first number in your Client ID)
var appId = ""
var pickerApiLoaded = false;
// Use the Google API Loader script to load the google.picker script.
function loadPicker() {
gapi.load('picker', {'callback': onPickerApiLoad});
}
function onPickerApiLoad() {
pickerApiLoaded = true;
createPicker();
}
// Create and render a Picker object
function createPicker() {
if (pickerApiLoaded) {
var view = new google.picker.DocsView();
view.setIncludeFolders(true);
//view.setMimeTypes("image/png,image/jpeg,image/jpg");
var picker = new google.picker.PickerBuilder()
//.enableFeature(google.picker.Feature.NAV_HIDDEN)
//.enableFeature(google.picker.Feature.MULTISELECT_ENABLED)
.setAppId(appId)
.setOAuthToken('<?= json_decode($client->getAccessToken())->access_token; ?>')
.addView(view)
.setDeveloperKey(developerKey)
.setCallback(pickerCallback)
.build();
picker.setVisible(true);
}
}
// A simple callback implementation.
function pickerCallback(data) {
if (data.action == google.picker.Action.PICKED) {
var fileId = data.docs[0].id;
document.getElementById("fileid").value = fileId;
}
}
</script>
</head>
<body>
<form action="quickstart.php" method="post">
<label for="fileid">File ID</label><input type="text" name="fileid" id="fileid">
<input type="submit">
</form>
<!-- The Google API Loader script. -->
<script type="text/javascript" src="https://apis.google.com/js/api.js?onload=loadPicker"></script>
<script type="text/javascript" src="https://apis.google.com/js/client.js"></script>
</body>
</html>
oauth2callback.php
Helper file for the OAuth callback.
<?php
require_once 'google-api-php-client/src/Google/autoload.php';
session_start();
$client = new Google_Client();
$client->setAuthConfigFile('client_secrets.json');
$client->setRedirectUri('http://localhost/drivepicker-php/oauth2callback.php');
$client->addScope(Google_Service_Drive::DRIVE_READONLY);
if (!isset($_GET['code'])) {
$auth_url = $client->createAuthUrl();
header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
} else {
$client->authenticate($_GET['code']);
$_SESSION['access_token'] = $client->getAccessToken();
$redirect_uri = 'http://localhost/drivepicker-php/quickstart.php';
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
?>
Upvotes: 7