phpgoogle-drive-api

Google drive API V3 make a copy of file to another drive without using service account using php


I'm trying to make copy of file (shared to anyone) into another google drive using php

code to create client object:

$this->client = new GClient();
$this->client->setApplicationName('Link Clone App');
$this->client->addScope(\Google\Service\Drive::DRIVE);
$this->client->setDeveloperKey(env('GOOGLE_API_KEY'));
// below line using this env var: GOOGLE_APPLICATION_CREDENTIALS=path/to/credential/json/file
// $this->client->useApplicationDefaultCredentials();

code to make copy of file:

try {
    // copy file
    $driveFile = new DriveFile();
    $copiedFile = $this->service->files->copy($originFileId, $driveFile, ['fields' => 'id,name,size,mimeType,webViewLink,webContentLink']);
    
    // create permission to anyone download
    $newPermission = new \Google\Service\Drive\Permission();
    $newPermission->setRole('reader');
    $newPermission->setType('anyone');
    
    // set permission to anyone
    $this->service->permissions->create($copiedFile->id, $newPermission);
    
    // get copied file with download link
    $newFile = $this->get($copiedFile->id);
    header("Location: {$newFile['webContentLink']}");
    exit();
} catch (\Exception $e) {
    return json_decode($e->getMessage());
}

when I use "GOOGLE_API_KEY" for authentication it will give below error when try to make copy:

    $this->client->setDeveloperKey(env('GOOGLE_API_KEY'));

   // error
    ^ {#75 ▼
          +"error": {#94 ▼
            +"errors": array:1 [▼
              0 => {#95 ▼
                +"domain": "global"
                +"reason": "required"
                +"message": "Login Required"
                +"locationType": "header"
                +"location": "Authorization"
              }
            ]
            +"code": 401
            +"message": "Login Required"
          }
        }

when I use service account credential json file for authentication it will create copy to service account. see the below code

// below line using this env var: GOOGLE_APPLICATION_CREDENTIALS=path/to/credential/json/file
    // $this->client->useApplicationDefaultCredentials();

what I want to do is: create a copy to my another google drive account and want to make file shared to anyone, but I didn't find any way to generate credential json file for my google drive account so I can use it.


Solution

  • The first thing you need to understand is the difference between private and public data.

    Public data is data that is now owned by anyone api keys are used to access public data. YouTube videos set to public are public data and can be accessed with an api key.

    Private data is data that is owned by a user, you need the user's permission to access it.

    methods like file.copy

    Tell you in the documentation that they need authorization

    enter image description here

    Which is why you get the Login Required error when you try to use them with a public api key.

    In the case of service accounts the access is pre authorized. If you don't want to use a service account then you will need to use Oauth2 and authorize the user

    <?php
    require __DIR__ . '/vendor/autoload.php';
    
    if (php_sapi_name() != 'cli') {
        throw new Exception('This application must be run on the command line.');
    }
    
    /**
     * Returns an authorized API client.
     * @return Google_Client the authorized client object
     */
    function getClient()
    {
        $client = new Google_Client();
        $client->setApplicationName('Google Drive API PHP Quickstart');
        $client->setScopes(Google_Service_Drive::DRIVE_METADATA_READONLY);
        $client->setAuthConfig('credentials.json');
        $client->setAccessType('offline');
        $client->setPrompt('select_account consent');
    
        // Load previously authorized token from a file, if it exists.
        // The file token.json stores the user's access and refresh tokens, and is
        // created automatically when the authorization flow completes for the first
        // time.
        $tokenPath = 'token.json';
        if (file_exists($tokenPath)) {
            $accessToken = json_decode(file_get_contents($tokenPath), true);
            $client->setAccessToken($accessToken);
        }
    
        // If there is no previous token or it's expired.
        if ($client->isAccessTokenExpired()) {
            // Refresh the token if possible, else fetch a new one.
            if ($client->getRefreshToken()) {
                $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
            } else {
                // Request authorization from the user.
                $authUrl = $client->createAuthUrl();
                printf("Open the following link in your browser:\n%s\n", $authUrl);
                print 'Enter verification code: ';
                $authCode = trim(fgets(STDIN));
    
                // Exchange authorization code for an access token.
                $accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
                $client->setAccessToken($accessToken);
    
                // Check to see if there was an error.
                if (array_key_exists('error', $accessToken)) {
                    throw new Exception(join(', ', $accessToken));
                }
            }
            // Save the token to a file.
            if (!file_exists(dirname($tokenPath))) {
                mkdir(dirname($tokenPath), 0700, true);
            }
            file_put_contents($tokenPath, json_encode($client->getAccessToken()));
        }
        return $client;
    }
    
    
    // Get the API client and construct the service object.
    $client = getClient();
    $service = new Google_Service_Drive($client);
    
    // Print the names and IDs for up to 10 files.
    $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());
        }
    }