Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is it possible to upload files to my own Google Drive using this SDK? #801

Closed
ghost opened this issue Jan 12, 2016 · 29 comments
Closed

Is it possible to upload files to my own Google Drive using this SDK? #801

ghost opened this issue Jan 12, 2016 · 29 comments

Comments

@ghost
Copy link

ghost commented Jan 12, 2016

Need to upload files on my PC to my own Google Drive (NOT to an end user's Google Drive) using PHP.
Is this possible using this SDK? If so, how?

@bshaffer
Copy link
Contributor

Yes you could use this library to accomplish this - just download Service Account Credentials and set the file path with $client->setAuthConfig.

However, Wouldn't you be better off using the Drive desktop client for that?

@ghost
Copy link
Author

ghost commented Jan 12, 2016

@bshaffer Thank you for your reply. Yes, I tried with Service account, however, its not giving me the same results as I have in my Google Drive. You can check the results here

And here is what I actually have in my Google Drive

Would you please explain why this happens?

@ghost
Copy link
Author

ghost commented Jan 12, 2016

@bshaffer Here is the source code I've written:

<?php
include_once __DIR__ . '/vendor/autoload.php';

$client = new Google_Client();

$credentialsFile = __DIR__ . '/credentials/service_account.json';

if (!file_exists($credentialsFile)) {
    throw new RuntimeException('Service account credentials Not Found!');
}

$client->setAuthConfig($credentialsFile);
$client->setApplicationName("Service Account Example");
$client->setScopes(Google_Service_Drive::DRIVE);

$service = new Google_Service_Drive($client);

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

print '<pre>';

foreach ($files as $file) {
    print_r($file);
    print PHP_EOL . '--------------' . PHP_EOL;
}

print '</pre>';

@bshaffer
Copy link
Contributor

What results are you getting? It's probably because you need to set the subject. Try doing the following:

$client->setSubject('yourdriveemail@gmail.com');

I am surprised the API isn't just throwing an error. Is the response coming back with an empty array for files?

@ghost
Copy link
Author

ghost commented Jan 12, 2016

Not an empty array... It contains only one file.

@ghost
Copy link
Author

ghost commented Jan 12, 2016

@bshaffer I got an exception when I do that.
Please help me correct the issue. https://googledrive-sunlean.c9users.io/

@ghost
Copy link
Author

ghost commented Jan 12, 2016

@bshaffer it looks like using the setSubject() method is treated as an impersonation and that's the cause of the error. Does this mean every service account has its own google drive instance that is independent of the owner (Me)?

@ghost
Copy link
Author

ghost commented Jan 12, 2016

@bshaffer After getting rid of the setSubject() line, I tried to insert some files and it worked just fine. it's just working on a mysterious drive :) I want it to work on MY OWN drive. Is this one of the issues that the latest SDK has? Maybe I might have to check out an older SDK.

@ghost
Copy link
Author

ghost commented Jan 12, 2016

@bshaffer As long as I am using a @gmail.com account, I don't need to create a Google App account in order to do this, do I?

@bshaffer
Copy link
Contributor

I'm not sure why you're seeing different files - I was under the impression it was impossible to upload to drive without a user context.

We are doing something here that might be useful. We set accessType to offline, and once the user authorizes, it comes back with a refresh token that gets automatically refreshed each time.

// create an authorized client
$client = new Google_Client();
$client->setClientId($clientId);
$client->setClientSecret($clientSecret);

// create file cache for storing the access token and refresh token
$cache = new Google_Cache_File(sys_get_temp_dir().'/google-api-php-client-tests');

// make sure we have a token
if (!$token = $cache->get('access_token')) {
  $client->setRedirectUri("urn:ietf:wg:oauth:2.0:oob");
  $client->setConfig('access_type', 'offline');
  $authUrl = $client->createAuthUrl();

  echo "\nPlease enter the auth code:\n";
  ob_flush();
  `open '$authUrl'`;
  $authCode = trim(fgets(STDIN));

  $token = $client->fetchAccessTokenWithAuthCode($authCode);
  if (!isset($token['access_token'])) {
    exit('failed!');
  }
  $cache->set('access_token', $token);
}

$client->setAccessToken($token);

@ghost
Copy link
Author

ghost commented Jan 13, 2016

@bshaffer I actually referred to this example that exists in the SDK.

https://github.com/google/google-api-php-client/blob/master/examples/service-account.php

For oAuth server-to-server interaction, I referred to this:

https://developers.google.com/api-client-library/php/auth/service-accounts

Probably the example is not for the right case?

@bshaffer
Copy link
Contributor

The second link there is referring to version 1 of this library, and has changed according to the Upgrading document in version 2. I suggest you stick with the first example.

We'll be moving the docs to version 2 very soon.

@ghost
Copy link
Author

ghost commented Jan 13, 2016

@bshaffer I know that, but the example should work for my case. Can you give me sample code that do the job for me? I need it done ASAP... I know this is out of your interest/service but I'd like to ask you to for me :) I was not able to get the API to work for my case or to find any examples on the Internet that works with Google Drive. I have to use the service account because it shouldn't be asking me to log in.

@bshaffer
Copy link
Contributor

I have already provided several code samples that will accomplish this task. In addition, there is this example that exists as part of the repository which accomplishes what you are trying to do.

@ghost
Copy link
Author

ghost commented Jan 13, 2016

@bshaffer This is not using service accounts. I wrote the code that combines the 2 examples (service account and file uploading) as you can see. As this reference says, I thought service accounts should work against my own google drive. Instead, it works against another mysterious google drive space that's independent of my drive and owned by that service account, which makes it impossible to access the files with my own google account. Is that a problem with the PHP SDK? Should I try version 1 or other SDK in other languages?

@bshaffer
Copy link
Contributor

bshaffer commented Jan 13, 2016

You have two options:

1. Create an "installed application" and use a refresh token

This is what I outlined in my code sample above. This does not use a service account, but instead uses an Oauth "installed app" which fetches the access token with a refresh token indefinitely

2. Create a "service account" and from your USER account, authorize the client so it shows up here

this is trickier, as you will need to create an OAuth Client just to authorize your user

go to API Manager > Credentials > New Credentials > OAuth Client ID and then select other as the client type.
screen shot 2016-01-13 at 11 59 46 am

Now download those credentials (below, client_secret_xxx.json) and use them like this:

$client->setAuthConfig('/path/to/client_secret_xxx.json');
$client->addScope("https://www.googleapis.com/auth/drive");
$client->setRedirectUri("urn:ietf:wg:oauth:2.0:oob"); // this is so you can grab the code in the browser
header('Location: '. $client->createAuthUrl());exit;

now you can copy the code and exchange it for an access token like this:

$client->fetchAccessTokenWithAuthCode($code);

It is not enough for you to authorize the client in the browser, you must also have the client request an access token to complete the authorization.

After all this, you will see the client show up under your authorized clients:

screen shot 2016-01-13 at 11 59 03 am

NEXT, you can switch back to your service account credentials and set the subject as the user who has authorized the app:

$client->setAuthConfig('/path/to/service-account-credentials.json');
$client->addScope("https://www.googleapis.com/auth/drive");
$client->setSubject('youremail@gmail.com');

// continue to upload with Drive forever and ever...

@Gummibeer
Copy link

@bshaffer Thanks a lot for your detailed explanation! 👍
It allowed me to refactor an old Adapter for laravel piece by piece.

@AndreyNazarchuk
Copy link

@bshaffer Now it says 'Requested client not authorized', even though I just pushed accept and copied the code and put it in the fetch function!

@bshaffer
Copy link
Contributor

bshaffer commented May 7, 2016

@AndreyNazarchuk does the authorized application show up in your authorized applications? If not, I assume the access token was not fetched as expected.

@AndreyNazarchuk
Copy link

@bshaffer It did once, then after that I cant get it to show up again.

@AndreyNazarchuk
Copy link

@bshaffer How can I ensure token is fetched as expected?

@bshaffer
Copy link
Contributor

bshaffer commented May 9, 2016

If the line $client->fetchAccessTokenWithAuthCode($code); returned an access token.

@ahait
Copy link

ahait commented Nov 21, 2016

@bshaffer Your post saved my day!! I've been trying all kinds of things, and couldnt make it work as I wanted to, and your step-by-step guide has just been wonderful!! Thank YOU! After almost 10 hours of work with this: THANK YOU !

@abhay1003
Copy link

abhay1003 commented Jan 21, 2017

hey, I am bigner in PHP and i want to upload csv/pdf file in drive programatically.

namespace GoogleApi;

require 'GoogleApi/Client.php';
require 'GoogleApi/Contrib/apiOauth2Service.php';
require 'GoogleApi/Contrib/apiDriveService.php';

$pdfFile = 'files/pdf.pdf';
//$pdfFile = 'files/report.csv';

// API Console: https://code.google.com/apis/console/
// Create an API project ("web applications") and put the client id and client secret in config.ini.
// Set up the Drive SDK in the API console.
// Create a Chrome extension, set the "container" and "api_console_project_id" parameters, and install it.
$config = parse_ini_file('php.ini'); // client_id, client_secret

// initialise the client
$client = new Client();
$client->setUseObjects(true);
$client->setAuthClass('apiOAuth2');
$client->setScopes(array('https://www.googleapis.com/auth/drive.file'));
$client->setClientId($config['932339130642-s0t08hcbtvbnp8e6e9f9q7c2gdkba7hn.apps.googleusercontent.com']);
$client->setClientSecret($config['u1cKtQ2b6iQ5zZJF7zYNQrUu']);
$client->setRedirectUri($config['http://localhost/test/driver_data']);
$client->setAccessToken(authenticate($client));

// initialise the Google Drive service
$service = new apiDriveService($client);

// create and upload a new Google Drive file, including the data
try {
	$file = new DriveFile;
	$file->setTitle(basename($pdfFile));
	$file->setMimeType('application/pdf');
	
	$result = $service->files->insert($file, array('data' => file_get_contents($pdfFile), 'mimeType' => 'application/pdf'));
}
catch (Exception $e) {
	print $e->getMessage();
}

print_r($result);

function authenticate($client, $file = 'token.json'){
	if (file_exists($file)) return file_get_contents($file);

	$_GET['code'] = ''; // insert the verification code here

	// print the authentication URL
	if (!$_GET['code']) {
		exit($client->createAuthUrl(array('https://www.googleapis.com/auth/drive.file')) . "\n");
	}

	file_put_contents($file, $client->authenticate());
	exit('Authentication saved to token.json - now run this script again.');
}

I am using above code but not working...Please help me.

Thank You

@bshaffer
Copy link
Contributor

@abhay1003 Please open a new issue with your question, as this issue has been closed.

@rmtngh
Copy link

rmtngh commented Oct 8, 2017

@bshaffer
Hi Brent,
Thanks for your detailed explanation, however I tried all the steps and I could get the app to show up in my account's authorized clients page, But I still get "unauthorized_client" error when I switch to service account's credentials and try to impersonate the account for which I authorized the app.
$client->setSubject('<ACCOUNT_THAT_AUTHORIZED_APP>@gmail.com');
I tried deleting service account, shutting down the project, starting everything over again, changing service account permissions (owner, editor, ...)
Is there anything else I can try?

@ghost
Copy link

ghost commented Nov 19, 2017

I am having the exact same issue.

  1. I got the code

  2. Successfully used it to fetch a token

  3. Verified that my app is listed in the "Third-party apps" for my personal gmail account.

  4. Use the service account json file and successfully call:
    putenv("GOOGLE_APPLICATION_CREDENTIALS=/path/to/my/service/account/json/file.json");
    $client = new Google_Client();
    $client->useApplicationDefaultCredentials();
    $client->addScope(Google_Service_Drive::DRIVE);
    $client->setSubject("my personal gmail address");
    $service = new Google_Service_Drive($client);

  5. But then when I try to call the below to list the files, I get an authentication error:
    $optParams = array( 'pageSize' => 10, 'fields' => 'nextPageToken, files(id, name)' );
    $results = $service->files->listFiles($optParams);

Fatal error: Uncaught exception 'Google_Service_Exception' with message '{
"error": "unauthorized_client",
"error_description": "Client is unauthorized to retrieve access tokens using this method."
}

Any idea what I might be doing wrong?
Does this only work for GSuite accounts and not regular personal Gmail accounts.

Thanks.

@0x42h
Copy link

0x42h commented May 10, 2018

@ignacvucko; my best guess is;

Does this only work for GSuite accounts and not regular personal Gmail accounts.

Yes, this is probably so, but let me check tomorrow.

@Luarb
Copy link

Luarb commented Jan 13, 2020

My problem is even more strange. I managed to use the service account authorization, but all the files and folders that I create are hidden from My Drive dashboard. When I use this code

$optParams = array( 'pageSize' => 10, 'fields' => 'nextPageToken, files(id, name)' ); $results = $service->files->listFiles($optParams); if (count($results->getFiles()) == 0) { print "No files found.\n"; } else { print "Files:\n"; foreach ($results->getFiles() as $file) { printf("%s (%s)\n", $file->getName(), $file->getId()); } }

all the files & folders get listed like this
Files: Hello World 3! (1R9EV0wrBZ87NgW3DH5pwZd398yRtwSTt) My Folder 3 (1S1xhdatA3Ai-GlnfMhwYysgsbsdzjQTy) Hello World 23! (1LMKPBoG5YxG30VRzr6Sh3FtWjuvqbrfx) My Folder 23 (12FRTA26RqBia0VccBla_O9THSNdMarop) Hello World 23! (1fbWwApNlQNwSbkzzXJ9Bs5iWA0TvBMgq) My Folder 23 (1jt2SGEz8bl9eAfO29CJnDYC9JWq-3SiJ) Hello World 23! (1yB88O2f5H7zozvBgzLzUSLPkC3JAeE5X) My Folder 23 (1YvVKnwNa9V68Hs01UrEQ_NXwzmXgS-FF) Hello World 3! (1s7cB1iQisYV3At_7rkhEQLyzcHTYEFvW) My Folder 3 (1CEbxkQNUYZZtBkCQy7P7F0AMeAgD8mns)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants