Code Monkey home page Code Monkey logo

flysystem-google-drive-ext's Introduction

Flysystem adapter for Google Drive with seamless virtual<=>display path translation

Flysystem API version Latest Version on Packagist License Build Status StyleCI Total Downloads

Google uses unique IDs for each folder and file. This makes it difficult to integrate with other storage services which use normal paths.

This Flysystem adapter works around that problem by seamlessly translating paths from "display paths" to "virtual paths", and vice versa.

For example: virtual path /Xa3X9GlR6EmbnY1RLVTk5VUtOVkk/0B3X9GlR6EmbnY1RLVTk5VUtOVkk becomes /My Nice Dir/myFile.ext and all ID handling is hidden.

Installation

  • For Flysystem V2/V3 or Laravel >= 9.x.x
composer require masbug/flysystem-google-drive-ext
  • For Flysystem V1 or Laravel <= 8.x.x use 1.x.x version of the package
composer require masbug/flysystem-google-drive-ext:"^1.0.0"

Getting Google Keys

Please follow Google Docs to obtain your client ID, client secret & refresh token.

In addition you can also check these easy-to-follow tutorial by @ivanvermeyen

Usage

$client = new \Google\Client();
$client->setClientId([client_id]);
$client->setClientSecret([client_secret]);
$client->refreshToken([refresh_token]);
$client->setApplicationName('My Google Drive App');

$service = new \Google\Service\Drive($client);

// variant 1
$adapter = new \Masbug\Flysystem\GoogleDriveAdapter($service, 'My_App_Root');

// variant 2: with extra options and query parameters
$adapter2 = new \Masbug\Flysystem\GoogleDriveAdapter(
    $service,
    'My_App_Root',
    [
        'useDisplayPaths' => true, /* this is the default */

        /* These are global parameters sent to server along with per API parameters. Please see https://cloud.google.com/apis/docs/system-parameters for more info. */
        'parameters' => [
            /* This example tells the remote server to perform quota checks per unique user id. Otherwise the quota would be per client IP. */
            'quotaUser' => (string)$some_unique_per_user_id
        ]
    ]
);

// variant 3: connect to team drive
$adapter3 = new \Masbug\Flysystem\GoogleDriveAdapter(
    $service,
    'My_App_Root',
    [
        'teamDriveId' => '0GF9IioKDqJsRGk9PVA'
    ]
);

// variant 4: connect to a folder shared with you
$adapter4 = new \Masbug\Flysystem\GoogleDriveAdapter(
    $service,
    'My_App_Root',
    [
        'sharedFolderId' => '0GF9IioKDqJsRGk9PVA'
    ]
);

$fs = new \League\Flysystem\Filesystem($adapter, new \League\Flysystem\Config([\League\Flysystem\Config::OPTION_VISIBILITY => \League\Flysystem\Visibility::PRIVATE]));
// List selected root folder contents
$contents = $fs->listContents('', true /* is_recursive */);

// List specific folder contents
$contents = $fs->listContents('MyFolder', true /* is_recursive */);
File upload
// Upload a file
$local_filepath = '/home/user/downloads/file_to_upload.ext';
$remote_filepath = 'MyFolder/file.ext';

$localAdapter = new \League\Flysystem\Local\LocalFilesystemAdapter('/');
$localfs = new \League\Flysystem\Filesystem($localAdapter, [\League\Flysystem\Config::OPTION_VISIBILITY => \League\Flysystem\Visibility::PRIVATE]);

try {
    $time = Carbon::now();
    $fs->writeStream($remote_filepath, $localfs->readStream($local_filepath), new \League\Flysystem\Config());

    $speed = !(float)$time->diffInSeconds() ? 0 :filesize($local_filepath) / (float)$time->diffInSeconds();
    echo 'Elapsed time: '.$time->diffForHumans(null, true).PHP_EOL;
    echo 'Speed: '. number_format($speed/1024,2) . ' KB/s'.PHP_EOL;
} catch(\League\Flysystem\UnableToWriteFile $e) {
    echo 'UnableToWriteFile!'.PHP_EOL.$e->getMessage();
}

// NOTE: Remote folders are automatically created.
File download
// Download a file
$remote_filepath = 'MyFolder/file.ext';
$local_filepath = '/home/user/downloads/file.ext';

$localAdapter = new \League\Flysystem\Local\LocalFilesystemAdapter('/');
$localfs = new \League\Flysystem\Filesystem($localAdapter, [\League\Flysystem\Config::OPTION_VISIBILITY => \League\Flysystem\Visibility::PRIVATE]);

try {
    $time = Carbon::now();
    $localfs->writeStream($local_filepath, $fs->readStream($remote_filepath), new \League\Flysystem\Config());

    $speed = !(float)$time->diffInSeconds() ? 0 :filesize($local_filepath) / (float)$time->diffInSeconds();
    echo 'Elapsed time: '.$time->diffForHumans(null, true).PHP_EOL;
    echo 'Speed: '. number_format($speed/1024,2) . ' KB/s'.PHP_EOL;
} catch(\League\Flysystem\UnableToWriteFile $e) {
    echo 'UnableToWriteFile!'.PHP_EOL.$e->getMessage();
}
How to get TeamDrive list and IDs
$drives = $fs->getAdapter()->getService()->teamdrives->listTeamdrives()->getTeamDrives();
foreach ($drives as $drive) {
    echo 'TeamDrive: ' . $drive->name . PHP_EOL;
    echo 'ID: ' . $drive->id . PHP_EOL. PHP_EOL;
}
How permanently deletes all of the user's trashed files
$fs->getAdapter()->emptyTrash([]);

Using with Laravel Framework

Update .env file with google keys

Add the keys you created to your .env file and set google as your default cloud storage. You can copy the .env.example file and fill in the blanks.

FILESYSTEM_CLOUD=google
GOOGLE_DRIVE_CLIENT_ID=xxx.apps.googleusercontent.com
GOOGLE_DRIVE_CLIENT_SECRET=xxx
GOOGLE_DRIVE_REFRESH_TOKEN=xxx
GOOGLE_DRIVE_FOLDER=
#GOOGLE_DRIVE_TEAM_DRIVE_ID=xxx
#GOOGLE_DRIVE_SHARED_FOLDER_ID=xxx

# you can use more accounts, only add more configs
#SECOND_GOOGLE_DRIVE_CLIENT_ID=xxx.apps.googleusercontent.com
#SECOND_GOOGLE_DRIVE_CLIENT_SECRET=xxx
#SECOND_GOOGLE_DRIVE_REFRESH_TOKEN=xxx
#SECOND_GOOGLE_DRIVE_FOLDER=backups
#SECOND_DRIVE_TEAM_DRIVE_ID=xxx
#SECOND_DRIVE_SHARED_FOLDER_ID=xxx
Add disks on config/filesystems.php
'disks' => [
    // ...
    'google' => [
        'driver' => 'google',
        'clientId' => env('GOOGLE_DRIVE_CLIENT_ID'),
        'clientSecret' => env('GOOGLE_DRIVE_CLIENT_SECRET'),
        'refreshToken' => env('GOOGLE_DRIVE_REFRESH_TOKEN'),
        'folder' => env('GOOGLE_DRIVE_FOLDER'), // without folder is root of drive or team drive
        //'teamDriveId' => env('GOOGLE_DRIVE_TEAM_DRIVE_ID'),
        //'sharedFolderId' => env('GOOGLE_DRIVE_SHARED_FOLDER_ID'),
    ],
    // you can use more accounts, only add more disks and configs on .env
    // also you can use the same account and point to a diferent folders for each disk
    /*'second_google' => [
        'driver' => 'google',
        'clientId' => env('SECOND_GOOGLE_DRIVE_CLIENT_ID'),
        'clientSecret' => env('SECOND_GOOGLE_DRIVE_CLIENT_SECRET'),
        'refreshToken' => env('SECOND_GOOGLE_DRIVE_REFRESH_TOKEN'),
        'folder' => env('SECOND_GOOGLE_DRIVE_FOLDER'),
    ],*/
    // ...
],
Add driver storage in a ServiceProvider on path app/Providers/

Example:

namespace App\Providers;

use Illuminate\Support\Facades\Storage;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider { // can be a custom ServiceProvider
    // ...
    public function boot(){
        // ...
        try {
            \Storage::extend('google', function($app, $config) {
                $options = [];

                if (!empty($config['teamDriveId'] ?? null)) {
                    $options['teamDriveId'] = $config['teamDriveId'];
                }

                if (!empty($config['sharedFolderId'] ?? null)) {
                    $options['sharedFolderId'] = $config['sharedFolderId'];
                }

                $client = new \Google\Client();
                $client->setClientId($config['clientId']);
                $client->setClientSecret($config['clientSecret']);
                $client->refreshToken($config['refreshToken']);
                
                $service = new \Google\Service\Drive($client);
                $adapter = new \Masbug\Flysystem\GoogleDriveAdapter($service, $config['folder'] ?? '/', $options);
                $driver = new \League\Flysystem\Filesystem($adapter);

                return new \Illuminate\Filesystem\FilesystemAdapter($driver, $adapter);
            });
        } catch(\Exception $e) {
            // your exception handling logic
        }
        // ...
    }
    // ...
}

Now you can access the drives like so:

$googleDisk = Storage::disk('google');
//$secondDisk = Storage::disk('second_google'); //others disks

Keep in mind that there can only be one default cloud storage drive, defined by FILESYSTEM_CLOUD in your .env (or config) file. If you set it to google, that will be the cloud drive:

Storage::cloud(); // refers to Storage::disk('google')

Limitations

Using display paths as identifiers for folders and files requires them to be unique. Unfortunately Google Drive allows users to create files and folders with same (displayed) names. In such cases when unique path cannot be determined this adapter chooses the oldest (first) instance. In case the newer duplicate is a folder and user puts a unique file or folder inside the adapter will be able to reach it properly (because full path is unique).

Concurrent use of same Google Drive might lead to unexpected problems due to heavy caching of file/folder identifiers and file objects.

Acknowledgements

This adapter is based on wonderful flysystem-google-drive by Naoki Sawada.

It also contains an adaptation of Google_Http_MediaFileUpload by Google. I've added support for resumable uploads directly from streams (avoiding copying data to memory).

TeamDrive support was implemented by Maximilian Ruta - Deltachaos.

Adapter rewrite for Flysystem V2 and various fixes were implemented by Erik Niebla - erikn69.

flysystem-google-drive-ext's People

Contributors

cruiser13 avatar deltachaos avatar dragermrb avatar elzdave avatar erikn69 avatar eusonlito avatar freimuts avatar mark-h avatar masbug avatar mikemand avatar propaganistas avatar stylecibot avatar thotam avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

flysystem-google-drive-ext's Issues

FileNotFoundException with sanitize_chars in directory name

FileNotFoundException will raise if sanitize_chars exist in directory name

Sample path to reproduce

$remote_filepath = '[test]test/test.txt';

I have passed sanitize_chars without [ and ] in options of Masbug\Flysystem\GoogleDriveAdapter, but doesn't help.

Logs League\Flysystem\FileNotFoundException

File not found at path: [test]test

at \vendor\masbug\flysystem-google-drive-ext\src\GoogleDriveAdapter.php:1773
1769▕ if($makeFullVirtualPath)
1770▕ return $this->makeFullVirtualPath($displayPath, $returnFirstItem);
1771▕
1772▕ if(empty($this->cachedPaths[$displayPath]))
➜ 1773▕ throw new FileNotFoundException($displayPath);
1774▕
1775▕ return $this->returnSingle($this->cachedPaths[$displayPath], $returnFirstItem);
1776▕ }
1777▕

1 \vendor\masbug\flysystem-google-drive-ext\src\GoogleDriveAdapter.php:1824
Masbug\Flysystem\GoogleDriveAdapter::toVirtualPath("[test]test")

2 \vendor\masbug\flysystem-google-drive-ext\src\GoogleDriveAdapter.php:297
Masbug\Flysystem\GoogleDriveAdapter::toSingleVirtualPath("[test]test")

PHP Warning: Invalid argument supplied for foreach() in vendor/masbug/flysystem-google-drive-ext/src/GoogleDriveAdapter.php on line 1104

when teamdrive is enabled i start getting these warnings when checking directory existence via Storage::disk('google')->getAdapter()->hasDir('directory')

also get the same warnings from flysystem-google-drive-ext/src/GoogleDriveAdapter.php on line 981

looks like $object->getPermissions() in function normaliseObject and $file->getPermissions() in function getRawVisibility are both returning null

Laravel 10 support

Hey guys, just wanted to confirm whether Laravel 10 is currently supported. Recently upgraded from 9 => 10 a few days ago and got this error during my backup cleanups:

Unable to retrieve the last_modified for file at location: staging-backup-2023-04-09-04-00-49.zip. backup::notifications.exception_message_trace #0 /var/www/html/vendor/league/flysystem/src/UnableToRetrieveMetadata.php(29): League\Flysystem\UnableToRetrieveMetadata::create() #1 /var/www/html/vendor/masbug/flysystem-google-drive-ext/src/GoogleDriveAdapter.php(863): League\Flysystem\UnableToRetrieveMetadata::lastModified() #2 /var/www/html/vendor/masbug/flysystem-google-drive-ext/src/GoogleDriveAdapter.php(893): Masbug\Flysystem\GoogleDriveAdapter->fileAttributes() #3 /var/www/html/vendor/league/flysystem/src/Filesystem.php(140): Masbug\Flysystem\GoogleDriveAdapter->lastModified() #4 /var/www/html/vendor/laravel/framework/src/Illuminate/Filesystem/FilesystemAdapter.php(623): League\Flysystem\Filesystem->lastModified()

Other than that I haven't ran into any others errors during backups. I've checked permissions, all good there. Perhaps a corrupt backup?

Example of path abstraction?

Hi there!

Thank you for this bundle, it's very interesting and promising!

I've tested it, successfully uploaded a doc to a specific parent folder but i have to use hashes and only one hash. If i try to have a folder structure folder_name/subfolder/file.txt Google SDK throws an exception File not found: folder_name. I suspected a shared folder as root to be the source of the issue but even if i try with a non-shared folder i have the same error.

I also tried to use IDs instead of "path abstractions" (eg: 12ESDFS/93JKDLDFK/file.txt) but it throws the same error.

Would it be possible to have an example to manage the folders path abstraction or direct file upload in a subfolder please? I'm sad to miss the fun of the package if this doesn't work.

Thank you very much for your time! 🙏

Duplicate root directories created when instantiating the adapter

A new My_App_Root directory is created each time I run the following code. I end up with multiple My_App_Root directories, with another being added each time I run the following code.

    $adapter = new \Masbug\Flysystem\GoogleDriveAdapter(
            $service,
            'My_App_Root',
            [
                'useDisplayPaths' => true,
                'teamDriveId' => '1ieb52EPLufHJrKIblaac4CYPLvtR91Rv'
            ]
        );

For example, the teamDriveId is a directory named test.

When I instantiate $adapter (And no other code after it), the following directory is created: test/My_App_Root.

Then if I run it again, a duplicate My_App_Root directory is created.

When viewing in Google Drive web interface, I see several My_App_Root directories in the test directory.

Am I doing something wrong? My goal is to list/read/write files to the test directory.

Invalid value at 'payload' (Map) ?

Hi. Any ideea about that?

  "error": {
    "code": 400,
    "message": "Invalid value at 'payload' (Map), Cannot bind a list to map for field 'exportLinks'.",
    "errors": [
      {
        "message": "Invalid value at 'payload' (Map), Cannot bind a list to map for field 'exportLinks'.",
        "reason": "invalid"
      }
    ],
    "status": "INVALID_ARGUMENT",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.BadRequest",
        "fieldViolations": [
          {
            "field": "payload",
            "description": "Invalid value at 'payload' (Map), Cannot bind a list to map for field 'exportLinks'."
          }
        ]
      }
    ]
  }
}
 {"exception":"[object] (Google\\Service\\Exception(code: 400): {
  \"error\": {
    \"code\": 400,
    \"message\": \"Invalid value at 'payload' (Map), Cannot bind a list to map for field 'exportLinks'.\",
    \"errors\": [
      {
        \"message\": \"Invalid value at 'payload' (Map), Cannot bind a list to map for field 'exportLinks'.\",
        \"reason\": \"invalid\"
      }
    ],
    \"status\": \"INVALID_ARGUMENT\",
    \"details\": [
      {
        \"@type\": \"type.googleapis.com/google.rpc.BadRequest\",
        \"fieldViolations\": [
          {
            \"field\": \"payload\",
            \"description\": \"Invalid value at 'payload' (Map), Cannot bind a list to map for field 'exportLinks'.\"
          }
        ]
      }
    ]
  }
}
 at C:\\app\\vendor\\google\\apiclient\\src\\Http\\REST.php:134)
[stacktrace]
#0 C:\\app\\vendor\\google\\apiclient\\src\\Http\\REST.php(107): Google\\Http\\REST::decodeHttpResponse()
#1 [internal function]: Google\\Http\\REST::doExecute()
#2 C:\\app\\vendor\\google\\apiclient\\src\\Task\\Runner.php(187): call_user_func_array()
#3 C:\\app\\vendor\\google\\apiclient\\src\\Http\\REST.php(66): Google\\Task\\Runner->run()
#4 C:\\app\\vendor\\google\\apiclient\\src\\Client.php(916): Google\\Http\\REST::execute()
#5 C:\\app\\vendor\\google\\apiclient\\src\\Service\\Resource.php(238): Google\\Client->execute()
#6 C:\\app\\vendor\\google\\apiclient-services\\src\\Drive\\Resource\\Files.php(108): Google\\Service\\Resource->call()
#7 C:\\app\\vendor\\masbug\\flysystem-google-drive-ext\\src\\GoogleDriveAdapter.php(1378): Google\\Service\\Drive\\Resource\\Files->create()
#8 C:\\app\\vendor\\masbug\\flysystem-google-drive-ext\\src\\GoogleDriveAdapter.php(644): Masbug\\Flysystem\\GoogleDriveAdapter->createDir()
#9 C:\\app\\vendor\\league\\flysystem\\src\\Filesystem.php(95): Masbug\\Flysystem\\GoogleDriveAdapter->createDirectory()
#10 C:\\app\\vendor\\laravel\\framework\\src\\Illuminate\\Filesystem\\FilesystemAdapter.php(862): League\\Flysystem\\Filesystem->createDirectory()
#11 C:\\app\\app\\Services\\Utils\\Backup.php(152): Illuminate\\Filesystem\\FilesystemAdapter->makeDirectory()
#12 C:\\app\\app\\Listeners\\Backend\\Access\\BackupZipWasCreatedEventListener.php(19): App\\Services\\Utils\\Backup->encode()
#13 C:\\app\\vendor\\laravel\\framework\\src\\Illuminate\\Events\\Dispatcher.php(441): App\\Listeners\\Backend\\Access\\BackupZipWasCreatedEventListener->onZipWasCreated()
#14 C:\\app\\vendor\\laravel\\framework\\src\\Illuminate\\Events\\Dispatcher.php(249): Illuminate\\Events\\Dispatcher->Illuminate\\Events\\{closure}()
#15 C:\\app\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\helpers.php(435): Illuminate\\Events\\Dispatcher->dispatch()
#16 C:\\app\\vendor\\spatie\\laravel-backup\\src\\Tasks\\Backup\\BackupJob.php(311): event()
#17 C:\\app\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\helpers.php(740): Spatie\\Backup\\Tasks\\Backup\\BackupJob->Spatie\\Backup\\Tasks\\Backup\\{closure}()
#18 C:\\app\\vendor\\spatie\\laravel-backup\\src\\Tasks\\Backup\\BackupJob.php(310): rescue()
#19 C:\\app\\vendor\\spatie\\laravel-backup\\src\\Tasks\\Backup\\BackupJob.php(231): Spatie\\Backup\\Tasks\\Backup\\BackupJob->sendNotification()
#20 C:\\app\\vendor\\spatie\\laravel-backup\\src\\Tasks\\Backup\\BackupJob.php(166): Spatie\\Backup\\Tasks\\Backup\\BackupJob->createZipContainingEveryFileInManifest()
#21 C:\\app\\vendor\\spatie\\laravel-backup\\src\\Commands\\BackupCommand.php(58): Spatie\\Backup\\Tasks\\Backup\\BackupJob->run()
#22 C:\\app\\vendor\\laravel\\framework\\src\\Illuminate\\Container\\BoundMethod.php(36): Spatie\\Backup\\Commands\\BackupCommand->handle()
#23 C:\\app\\vendor\\laravel\\framework\\src\\Illuminate\\Container\\Util.php(41): Illuminate\\Container\\BoundMethod::Illuminate\\Container\\{closure}()
#24 C:\\app\\vendor\\laravel\\framework\\src\\Illuminate\\Container\\BoundMethod.php(93): Illuminate\\Container\\Util::unwrapIfClosure()
#25 C:\\app\\vendor\\laravel\\framework\\src\\Illuminate\\Container\\BoundMethod.php(35): Illuminate\\Container\\BoundMethod::callBoundMethod()
#26 C:\\app\\vendor\\laravel\\framework\\src\\Illuminate\\Container\\Container.php(661): Illuminate\\Container\\BoundMethod::call()
#27 C:\\app\\vendor\\laravel\\framework\\src\\Illuminate\\Console\\Command.php(194): Illuminate\\Container\\Container->call()
#28 C:\\app\\vendor\\symfony\\console\\Command\\Command.php(312): Illuminate\\Console\\Command->execute()
#29 C:\\app\\vendor\\laravel\\framework\\src\\Illuminate\\Console\\Command.php(163): Symfony\\Component\\Console\\Command\\Command->run()
#30 C:\\app\\vendor\\spatie\\laravel-backup\\src\\Commands\\BaseCommand.php(28): Illuminate\\Console\\Command->run()
#31 C:\\app\\vendor\\symfony\\console\\Application.php(1022): Spatie\\Backup\\Commands\\BaseCommand->run()
#32 C:\\app\\vendor\\symfony\\console\\Application.php(314): Symfony\\Component\\Console\\Application->doRunCommand()
#33 C:\\app\\vendor\\symfony\\console\\Application.php(168): Symfony\\Component\\Console\\Application->doRun()
#34 C:\\app\\vendor\\laravel\\framework\\src\\Illuminate\\Console\\Application.php(102): Symfony\\Component\\Console\\Application->run()
#35 C:\\app\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Console\\Kernel.php(155): Illuminate\\Console\\Application->run()
#36 C:\\app\\artisan(34): Illuminate\\Foundation\\Console\\Kernel->handle()
#37 C:\\app\\artisan(3): sg_load()
#38 {main}
"} ```

Get infomration about uploaded file when using Laravel

When uploading some file with Laravel like so:

Storage::disk('google')->writeStream('composer.json', fopen('composer.json', 'r')) // true

The output of writeStream is boolean, Is there another method that get more information about uploaded file ?

like file_id, created_at etc ... ?

masbug/flysystem-google-drive-ext: 2.1
laravel 9

[Action Required] Drive API requires updates to your code before Sep 13, 2021

Hello Google Drive Developer,

We have identified you as a Developer who has used the Drive API in the last 30 days. We are writing to let you know that on September 13, 2021, Drive will apply a security update that will change the links used to share some files, and may lead to some new file access requests. Access to files won’t change for people who have already viewed or modified these files.

Please update your code as detailed below before September 13, 2021, to avoid failing requests.

What do I need to know?

Items that have a Drive API permission with type=domain or type=anyone, where withLink=true (v2) or allowFileDiscovery=false (v3), will be affected by this security update.

In addition to the item ID, your application may now also need a resource key to access these items. Without a resource key, requests for these items may result in a 404 Not Found error (See below for details). Note that access to items that are directly shared with the user or group are not affected.

Will this change affect me?

If your application uses the Drive API to access files which have been shared with a user through link sharing, your application may be affected by this change.

What do I need to do?

To avoid errors accessing files, you must update your code for accessing files to include the appropriate resource keys. Details on how to do this for each of the affected Drive APIs is included below:

Changes to the Drive API
The resource key of an item is returned on the resourceKey field of the file metadata in the Drive API response.

  • If the file is a shortcut file, then the resource key for the target of the shortcut can be read from the shortcutDetails.targetResourceKey field of the same resource.
  • URL type fields such as exportLinks, webContentLink, and webViewLink will include the resourceKey.
  • Requests to the Drive API can specify one or more resource keys with the X-Goog-Drive-Resource-Keys HTTP request header.
    Learn more about this change from the Drive API guide.

Changes to Apps Script
The DriveApp from Apps Script has been updated to return the resource key of a file or folder with the getResourceKey method.

  • Note: When fetching a file or folder, the resource key can be specified on the getFileByIdAndResourceKey or getFolderByIdAndResourceKey methods.

Changes to Drive UI Integrations
If your application is integrated with the Drive UI to create or open items, it will receive resource keys when your application is invoked from the Drive UI.

  • The state information for a New URL will contain folderResourceKey, which is the resource key of the folder where the new item should be created.
  • The state for an Open URL will contain a mapping of file ID to resource key in the resourceKeys field.

Learn more about integrating with the Drive UI on our website.

listContents returns empty array

I migrated to Laravel 9 which uses Flysystem 3, making the nao-pon/flysystem-google-drive repository incompatible. In search of an alternative, I was happy to find out this one was built on his adapter.

First, I tried using it through the Storage facade:

$disk = \Storage::disk('google');
$dirs = $disk->allDirectories();

Returns an empty array, even though I have a couple directories in the root folder. The very same code works with Naoki Sawada's adapter, then the array is populated with the IDs of the directories.

I tried bypassing the facade: $content = $disk->getAdapter()->listContents('', true);, and I get the same.

My configuration is straight from your documentation, but I'm going to paste it here anyway.

fileysytems.php:

'google' => [
            'driver' => 'google',
            'clientId' => env('GOOGLE_DRIVE_CLIENT_ID'),
            'clientSecret' => env('GOOGLE_DRIVE_CLIENT_SECRET'),
            'refreshToken' => env('GOOGLE_DRIVE_REFRESH_TOKEN'),
            'folderId' => env('GOOGLE_DRIVE_FOLDER_ID'),
            'teamDriveId' => env('GOOGLE_DRIVE_TEAM_DRIVE_ID'),
        ],

All values are set and correct in .env.

In the boot() of GoogleDriveServiceProvider:

\Storage::extend('google', function($app, $config) {
            $options = [];

            if (!empty($config['teamDriveId'] ?? null)) {
                $options['teamDriveId'] = $config['teamDriveId'];
            }

            $client = new \Google\Client();
            $client->setClientId($config['clientId']);
            $client->setClientSecret($config['clientSecret']);
            $client->refreshToken($config['refreshToken']);

            $service = new \Google\Service\Drive($client);
            $adapter = new \Masbug\Flysystem\GoogleDriveAdapter($service, $config['folderId'] ?? '/', $options);
            $driver = new \League\Flysystem\Filesystem($adapter);

            return new \Illuminate\Filesystem\FilesystemAdapter($driver, $adapter);
        });

Am I missing something obvious? What else should I include?

Retrieve Objects Ids by default when useDisplayPaths is set to False

Hi I was reading through your code and I noticed you're using 'useDisplayPaths' param to prevent that feature but I wonder this:
In the function createDirectory I get nothing back so how would I know which Id was assigned by google to my directory? I saw from the tests you're calling getMetadata function to retrieve created object by name but it would cause me problems for duplicates. If I understood something wrong correct me, otherwise you could give me a small example how to create a folder and get back its id please.

creating a new folder in my drive

Hello, every time I refresh the token it is creating a new folder and using it as the FolderId even if I assigned one in the .env it still doesn't use the folder instead it uses the newly created one

How to move app to live

After we finished developing our app we are now trying to make the access token refresh automatically how is that possible for Oauth and the package?

I did not see using any access tokens

Shared folder

Hi,
how can I have access to shared folders ;

Thanks

Delete forever ?

When i use Storage::disk('google')->delete($file) it move the file into trash section, how can i delete file forever ?

directoryExists not defined

Hi,

Tried to check if folder exists to prevent multiple folders with the same name:

if(!$fs->directoryExists($directory))
       $fs->createDirectory($directory, ['visibility' => 'private']);

but it throws an error. Am I trying something wrong? Tried with fileExists but doesnt work for directories.

undefined method League\Flysystem\Filesystem::directoryExists()

Thanks again!

Can't display paths, just IDs

Using v2.2.2
Seems related to #94, except that I don't use Laravel, but barryvdh/elfinder-flysystem-driver, and this is for a personal drive, not team.

I also have the problem that listContents returns an empty list with useDisplayPaths set to true, and IDs when set to false.
I tried setting showDisplayPaths to true to no avail, and also all combinations I could think of, but I am not able to list the names of folders and files.

Here's the configuration I'm using, does anyone have any idea what's wrong?

$GDclient = new \Google_Client();
$GDclient->setClientId('...');
$GDclient->setClientSecret('...');
$GDclient->refreshToken('...');

new \Masbug\Flysystem\GoogleDriveAdapter(
	new \Google_Service_Drive($GDclient),
	'root',                             
	[
//		'useHasDir'     => true,
//		'driveId'       => 'root',
//		'useDisplayPaths'       => false,
//		'showDisplayPaths'      => true,
	]
),

Hooking into the cache

Thanks for your work on this driver :)

The adapter seems to be requesting all root files when initializing, which is quite the performance impact. Now, it does do a lot of in-memory caching, however, that cache is gone with the next request.

I've looked through the code, but is there supposed to be a way to hook into this cache to somehow persist it in my app's cache and restore the driver with pre-fetched data?

GoogleDriveAdapter => UnableToReadFile error

Context

Hello,
I'm running Laravel 9.22.1 with PHP 8.1 with those packages :

version
masbug/flysystem-google-drive-ext v2.2.2
spatie/laravel-backup v8.1.3

I created my credentials to access to Google Drive and I setup accordingly your readme my config files : .env, filesystems.php

My configuration works, if I check

dd(Storage::disk('google'));

I can see properties like driver, adapter

I'm trying to backup database with backup:run --only-db but I have this error in terminal :

Copying zip failed because: Could not connect to disk google because: League\Flysystem\UnableToReadFile: Unable to read file from location: projectv9_local_agency. File not found in /Volumes/Data/WEB/www-local/lab/projectv9/vendor/league/flysystem/src/UnableToReadFile.php:24

Stack trace:

#0 /Volumes/Data/WEB/www-local/lab/projectv9/vendor/masbug/flysystem-google-drive-ext/src/GoogleDriveAdapter.php(1904): League\Flysystem\UnableToReadFile::fromLocation('projectv9_lo...', 'File not found')

#1 /Volumes/Data/WEB/www-local/lab/projectv9/vendor/masbug/flysystem-google-drive-ext/src/GoogleDriveAdapter.php(2002): Masbug\Flysystem\GoogleDriveAdapter->makeFullVirtualPath('projectv9_lo...', false)

#2 /Volumes/Data/WEB/www-local/lab/projectv9/vendor/masbug/flysystem-google-drive-ext/src/GoogleDriveAdapter.php(808): Masbug\Flysystem\GoogleDriveAdapter->toVirtualPath('projectv9_lo...')

#3 /Volumes/Data/WEB/www-local/lab/projectv9/vendor/league/flysystem/src/DirectoryListing.php(33): Masbug\Flysystem\GoogleDriveAdapter->listContents('projectv9_lo...', false)

So how do I have to make it work ?

Thank you very much

Invalid Credentials (refresh token not working)

I thought this issue was only happen in this repo but also happens in nao package i described it here nao-pon/flysystem-google-drive#99

When i uploading for two hours, i mean i pushed 10 jobs in laravel to upload some files in different times, the first hour is ok and after that i will get :

{
 "error": {
  "errors": [
   {
    "domain": "global",
    "reason": "authError",
    "message": "Invalid Credentials",
    "locationType": "header",
    "location": "Authorization"
   }
  ],
  "code": 401,
  "message": "Invalid Credentials"
 }
}

I did exactly what was said in this article , i checked the "auto refresh token when expired" but somehow the token will expire after an hour, should i do extra work to refresh the access token ?

cannot list directory shortcuts

In My Google Drive I have a number of shortcuts to directories that have been shared with me.
I need to be able to upload files into these shared folders but currently
Storage::disk('google')->listContents('the-shortcut') just returns []

I've worked around it by forking the code and adding shortcutDetails to FETCHFIELDS_LIST and adding

if ($object->mimeType == self::SHORTCUTMIME) {
    $object->mimeType = $object->shortcutDetails->targetMimeType;
    $id = $object->shortcutDetails->targetId;
}

near the top of normaliseObject.
but it feels too easy & I wonder what I might be missing

Don't list files and Directories in shared folder

I can create folder at shared folder betwen two accounts but I can't show the files and directories in that folder.
Plessea tell me where is wrong.
Laravel Framework 10.13.5
PHP 8.1.10
Route::get('list-files', function() { $recursive = false; // Get subdirectories also? $contents = collect(Storage::cloud()->listContents('/', $recursive)); dd($contents->where('type', 'dir')); return $contents->where('type', 'dir'); // directories // return $contents->where('type', 'file')->mapWithKeys(function($file) { // return [$file->path() => pathinfo($file->path(),PATHINFO_BASENAME)]; // }); });

help with `backup:clean`

I know it's nothing to do with this package, But wondering why the command backup:clean from https://github.com/spatie/laravel-backup doesnt clean up the old backups ? Previously i was using https://github.com/nao-pon/flysystem-google-drive as google adapter but since it shows the files as raw string so i tought it was the issue then i installed this package, but still no luck the old file still remain in the storage 2021-07-03-17-14-36.zip

Starting cleanup...
Cleaning backups of  on disk local...
Used storage after cleanup: 0 B.
Cleaning backups of  on disk google...
Used storage after cleanup: 3.73 GB.
Cleanup completed!

Error in masbug because upgrade system Google API

how to solve
{ "error": { "code": 401, "message": "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.", "errors": [ { "message": "Login Required.", "domain": "global", "reason": "required", "location": "Authorization", "locationType": "header" } ], "status": "UNAUTHENTICATED", "details": [ { "@type": "type.googleapis.com/google.rpc.ErrorInfo", "reason": "CREDENTIALS_MISSING", "domain": "googleapis.com", "metadata": { "method": "google.apps.drive.v3.DriveFiles.Create", "service": "drive.googleapis.com" } } ] } }

In laravel

GoogleDriveAdapter FileNotFoundException

I'm having an error while using Storage::disk('google')->getDriver()->getAdapter()->getMetadata("sample.png"). It says file is not found at \/(virtual filename here). It always prepend "\/" which I think causes the error. But when I use Storage::disk('google')->getFiles(), it successfully displays all the files in the google drive folder so I'm pretty sure it can access the folder. What can be the problem in my case?

Unable to run backup spatie/laravel-backup

I am using Laravel 9. I can use Storage to put file into Google Drive but when I run artisan backup:run it throws expection.

Copying zip failed because: Could not connect to disk google because: League\Flysystem\UnableToReadFile: Unable to read file from location: launchlist. File not found in E:\dev\code\getlaunchlist\vendor\league\flysystem\src\UnableToReadFile.php:24 Stack trace: #0 E:\dev\code\getlaunchlist\vendor\masbug\flysystem-google-drive-ext\src\GoogleDriveAdapter.php(1869): League\Flysystem\UnableToReadFile::fromLocation('launchlist', 'File not found') #1 E:\dev\code\getlaunchlist\vendor\masbug\flysystem-google-drive-ext\src\GoogleDriveAdapter.php(1967): Masbug\Flysystem\GoogleDriveAdapter->makeFullVirtualPath('launchlist', false) #2 E:\dev\code\getlaunchlist\vendor\masbug\flysystem-google-drive-ext\src\GoogleDriveAdapter.php(805): Masbug\Flysystem\GoogleDriveAdapter->toVirtualPath('launchlist')#3 E:\dev\code\getlaunchlist\vendor\league\flysystem\src\DirectoryListing.php(33): Masbug\Flysystem\GoogleDriveAdapter->listContents('launchlist', true) #4 E:\dev\code\getlaunchlist\vendor\league\flysystem\src\DirectoryListing.php(46): League\Flysystem\DirectoryListing::League\Flysystem\{closure}(Object(Generator)) #5 [internal function]: League\Flysystem\DirectoryListing::League\Flysystem\{closure}(Object(Generator)) #6 E:\dev\code\getlaunchlist\vendor\league\flysystem\src\DirectoryListing.php(81): iterator_to_array(Object(Generator), false) #7 E:\dev\code\getlaunchlist\vendor\laravel\framework\src\Illuminate\Filesystem\FilesystemAdapter.php(728): League\Flysystem\DirectoryListing->toArray() #8 E:\dev\code\getlaunchlist\vendor\laravel\framework\src\Illuminate\Filesystem\FilesystemAdapter.php(739): Illuminate\Filesystem\FilesystemAdapter->files('launchlist', true) #9 E:\dev\code\getlaunchlist\vendor\spatie\laravel-backup\src\BackupDestination\BackupDestination.php(140): Illuminate\Filesystem\FilesystemAdapter->allFiles('launchlist') #10 E:\dev\code\getlaunchlist\vendor\spatie\laravel-backup\src\Tasks\Backup\BackupJob.php(286): Spatie\Backup\BackupDestination\BackupDestination->isReachable() #11 E:\dev\code\getlaunchlist\vendor\laravel\framework\src\Illuminate\Collections\Traits\EnumeratesValues.php(263): Spatie\Backup\Tasks\Backup\BackupJob->Spatie\Backup\Tasks\Backup\{closure}(Object(Spatie\Backup\BackupDestination\BackupDestination), 0) #12 E:\dev\code\getlaunchlist\vendor\spatie\laravel-backup\src\Tasks\Backup\BackupJob.php(304): Illuminate\Support\Collection->each(Object(Closure)) #13 E:\dev\code\getlaunchlist\vendor\spatie\laravel-backup\src\Tasks\Backup\BackupJob.php(168): Spatie\Backup\Tasks\Backup\BackupJob->copyToBackupDestinations('E:\\dev\\code\\get...') #14 E:\dev\code\getlaunchlist\vendor\spatie\laravel-backup\src\Commands\BackupCommand.php(58): Spatie\Backup\Tasks\Backup\BackupJob->run() #15 E:\dev\code\getlaunchlist\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php(36): Spatie\Backup\Commands\BackupCommand->handle() #16 E:\dev\code\getlaunchlist\vendor\laravel\framework\src\Illuminate\Container\Util.php(41): Illuminate\Container\BoundMethod::Illuminate\Container\{closure}() #17 E:\dev\code\getlaunchlist\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php(93): Illuminate\Container\Util::unwrapIfClosure(Object(Closure)) #18 E:\dev\code\getlaunchlist\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php(37): Illuminate\Container\BoundMethod::callBoundMethod(Object(Illuminate\Foundation\Application), Array, Object(Closure)) #19 E:\dev\code\getlaunchlist\vendor\laravel\framework\src\Illuminate\Container\Container.php(653): Illuminate\Container\BoundMethod::call(Object(Illuminate\Foundation\Application), Array, Array, NULL) #20 E:\dev\code\getlaunchlist\vendor\laravel\framework\src\Illuminate\Console\Command.php(136): Illuminate\Container\Container->call(Array) #21 E:\dev\code\getlaunchlist\vendor\symfony\console\Command\Command.php(291): Illuminate\Console\Command->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Illuminate\Console\OutputStyle)) #22 E:\dev\code\getlaunchlist\vendor\laravel\framework\src\Illuminate\Console\Command.php(121): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Illuminate\Console\OutputStyle)) #23 E:\dev\code\getlaunchlist\vendor\spatie\laravel-backup\src\Commands\BaseCommand.php(28): Illuminate\Console\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput)) #24 E:\dev\code\getlaunchlist\vendor\symfony\console\Application.php(989): Spatie\Backup\Commands\BaseCommand->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput)) #25 E:\dev\code\getlaunchlist\vendor\symfony\console\Application.php(299): Symfony\Component\Console\Application->doRunCommand(Object(Spatie\Backup\Commands\BackupCommand), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput)) #26 E:\dev\code\getlaunchlist\vendor\symfony\console\Application.php(171): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput)) #27 E:\dev\code\getlaunchlist\vendor\laravel\framework\src\Illuminate\Console\Application.php(102): Symfony\Component\Console\Application->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput)) #28 E:\dev\code\getlaunchlist\vendor\laravel\framework\src\Illuminate\Foundation\Console\Kernel.php(129): Illuminate\Console\Application->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput)) #29 E:\dev\code\getlaunchlist\artisan(37): Illuminate\Foundation\Console\Kernel->handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput)) #30 {main}.

Please help me fix this issue

getting display path with `useDisplayPath` set to `false`

I'm using folder/file's ID to set the root dir, list the folder contents, get files' contents etc etc. It seems that it requires setting useDisplayPaths to false but when I set this I'm not able to retrieve the file name from the file attributes that I want to show to the users.

Currently I've just extended the GoogleDriveAdapter class and overrode normaliseObject method to always set $result['display_path'] to $this->toDisplayPath($result['virtual_path']) regardless of the useDisplayPaths setting, but is there a better way to do it? If not that would be my feature request

I can't install this package in laravel 8

Problem 1
- Conclusion: don't install masbug/flysystem-google-drive-ext v2.0.0 (conflict analysis result)
- Root composer.json requires masbug/flysystem-google-drive-ext ^2.0 -> satisfiable by masbug/flysystem-google-drive-ext[v2.0.0, 2.x-dev].
- Conclusion: don't install league/flysystem 1.1.2 (conflict analysis result)
- Conclusion: don't install league/flysystem 2.2.0 (conflict analysis result)
- Conclusion: don't install league/flysystem 1.1.3 (conflict analysis result)
- Conclusion: don't install league/flysystem 2.2.1 (conflict analysis result)
- Conclusion: don't install league/flysystem 1.1.4 (conflict analysis result)
- Conclusion: don't install league/flysystem 2.2.2 (conflict analysis result)
- Conclusion: don't install league/flysystem 1.1.5 (conflict analysis result)
- Conclusion: don't install league/flysystem 2.2.3 (conflict analysis result)
- laravel/framework is locked to version v8.54.0 and an update of this package was not requested.
- laravel/framework v8.54.0 requires league/flysystem ^1.1 -> satisfiable by league/flysystem[1.1.0, ..., 1.x-dev].
- You can only install one version of a package, so only one of these can be installed: league/flysystem[1.1.0, ..., 1.x-dev, 2.1.1, ..., 2.x-dev].
- masbug/flysystem-google-drive-ext 2.x-dev requires league/flysystem ^2.1.1 -> satisfiable by league/flysystem[2.1.1, ..., 2.x-dev].
- Conclusion: don't install league/flysystem 2.1.1 (conflict analysis result)

Fix Shield on ReadMe.md

image

New ones have another branch's names
1.x image
1.x image
2.x image
2.x image

Since June 15th, 2021, the building on travis-ci.org is ceased. Please use travis-ci.com from now on.

Dir sharing issue

Method url() publish folder but do not return the link.
Link has the next template:
'https://drive.google.com/drive/folders/' . $id . '?usp=sharing'

Problem with spatie laravel backup - League\Flysystem\UnableToReadFile

Same exact problem as in issue #59 from the looks of it.

I've tried this both on Linux and Windows, same error:

 No se puede acceder al destino de la copia de seguridad. League\Flysystem\UnableToReadFile: Unable to read file from location: Dominar el ingles. File not found in /var/www/dominarelingles/current/vendor/league/flysystem/src/
UnableToReadFile.php:24

I haven't provided the stack trace but it looks the same as in the other referenced issue. If needed I will provide it.

I've done some debugging and stepping through the execution I'm able to see that it is indeed connecting successfully to Google Drive and listing all the folders in the root of my drive.

But I guess it's unable to find the folder "Dominar el ingles"? That's not even the name I want to give the folder, but that is the name of my application. So I created a folder with that exact name and placed its ID in the folderId parameter in the config. Here's my service provider:

class GoogleDriveServiceProvider extends ServiceProvider
{
    public function register()
    {
        //
    }

    public function boot()
    {
        Storage::extend('google', function ($app, $config) {
            $client = new Google_Client();
            $client->setClientId($config['clientId']);
            $client->setClientSecret($config['clientSecret']);
            $client->refreshToken($config['refreshToken']);
            $service = new Google_Service_Drive($client);
            $adapter = new GoogleDriveAdapter($service, $config['folderId']);

            return new FilesystemAdapter(new Filesystem($adapter), $adapter, $config);
        });
    }
}

Here's my filesystem config:

'disks' => [

        ...

        'google' => [
            'driver' => 'google',
            'clientId' => env('GOOGLE_DRIVE_CLIENT_ID'),
            'clientSecret' => env('GOOGLE_DRIVE_CLIENT_SECRET'),
            'refreshToken' => env('GOOGLE_DRIVE_REFRESH_TOKEN'),
            'folderId' => env('GOOGLE_DRIVE_FOLDER_ID'),
        ],

    ],

Here's my .env:

GOOGLE_DRIVE_CLIENT_ID="..."
GOOGLE_DRIVE_CLIENT_SECRET="..."
GOOGLE_DRIVE_REFRESH_TOKEN="..."
GOOGLE_DRIVE_FOLDER_ID="1u1x3azek-VBRCzTmGz0H8yst-PRd2vpn"

The folder ID is the ID to the folder I have named "Dominar el ingles"

In Spatie's backup config I have only changed everywhere that says 'disks' => ['local'], to 'disks' => ['google'],

Everything works if I use the local disk, but I cannot figure out why I'm getting this error for the google disk. Any ideas what I'm doing wrong?

Working with Shared Drives

Hello - I'm thinking this is less of an issue and more of just me not understanding but I'm trying to work within Shared Drives and not having much luck. The structure I'm going for is:

Shared Drive

  • Sub Folder 1
    • File 1
  • Sub Folder 2
    • File 1
    • File 2
      I've been able to use Storage::disk('google')->makeDirectory('Sub Folder 1') and that works fine. The problem comes when I try to use Storage::disk('google')->put('Sub Folder 1/File 1.txt', 'Hello World'); - In this case it ends up creating a "Sub Folder 1" and then I get the error in saying "Unable to read file from location: [shared drive id]/Sub Folder 1. File not found" and the folder it created is empty.

Essentially, I can’t do anything within folder inside a shared drive. I can only perform actions in the root of shared drive.

Thanks in advance!

Move Shared Drive file to a folder

I've got this simple Shared Drive structure:

Shared Drive
Folder 1
Folder 2
Folder 3

I'd like to create a file in one of the folders within that Shared Drive i.e.: Folder 1, Folder 2, etc.

Is it possible to define which folder I want to create the file in? Preferably by passing the folder name instead of the folder ID.

I already can create files in that Shared Drive, but I'd like to be able to create them in specific folders within that Shared Drive.

Hope that makes sense :)

When uploading a file to a shared folder, the file doesn't end up in the desired destination.

Greetings!

Thank you for your work on this extension! My team has been using the 1.x version for the last number of years, and recently released our upgrade to Laravel 9.x and the 2.x version of this package on the 14th of December.

We were alerted to an issue, where none of our reports have uploaded to a shared Google Drive folder since December 14th, on the 19th. I've been debugging it for the last day and I believe I've narrowed down the scope/cause to our upgrade to the 2.x version of this package

Setup

Our application uploads various reports to different FTP servers and a few Google Drive Folders. Our file systems can be summarised by

'google_drive_vendor' => [
  'driver' => 'google_drive',
  'root' => env('REPORT_FOLDER_ID'),
  'subject' => env('GOOGLE_DRIVE_USER', '[email protected]'),
  'service_account' => 'company-service-account'
],

The REPORT_FOLDER_ID is set to a value that looks similar to this (changed for privacy) 8KtgBylU4qfGRybyz0zbJSYqBrGRpp-dp. The value for a folder labeled Metrics, and owned by our VP of Engineering. The account that uploads the file is an automations account on our account used for our reporting.

Expectations

Before we upgraded to the 2.x version, our automation user would be able to successfully upload to the shared folder.

Using the previously mentioned report folder id as an example: This would upload a file to the folder with the ID 8KtgBylU4qfGRybyz0zbJSYqBrGRpp-dp, in this case a folder named Metrics, so the path should be /Metrics/file.txt -> Within shared folder

Current Behavior

With this upgrade to 2.x, our automation user is creating its own folder (not shared) labeled after the ID of the desired folder.
Using the previously mentioned report folder id as an example: it would create a folder named 8KtgBylU4qfGRybyz0zbJSYqBrGRpp-dp, and within that folder it uploads our files, instead of uploading to the folder that has that as its ID, in this case a folder named Metrics, so the path ends up being /8KtgBylU4qfGRybyz0zbJSYqBrGRpp-dp/file.txt -> Within the [email protected]'s drive folder.

Questions?

  • Is this functionality intentional? I don't see an explicit mention to uploading to shared drive folders, I saw this issue, but after attempting to upload a file with the changes made in the suggested PR, our problem persisted with the current behavior.
  • Is there a way to accomplish the expected behavior without reverting to 2.x? (I imagine we're missing some kind of config option, but am not entirely sure)
  • I'd love to help contribute! Is there anything I should know before attempting to submit a PR for a fix? (Like preferred coding patterns for the package, or a preferred way to test?)

Backup failed because: Unable to write file at location:

I'm trying to integrate laravel backups with a google team drive. I'm able to successfully create back ups locally on google drive folders I've created. However whenever I try to add a google drive team id, I keep running into this error whenever GoogleDriveAdapter->writeData is ran:

Starting backup...
Dumping database mydb...
Determining files to backup...
Zipping 1 files and directories...
Created zip containing 1 files and directories. Size is 14.28 MB
Copying zip to disk named google...
Copying zip failed because: Unable to write file at location: local-backup-2023-03-06-14-50-06.zip. Not able to write the file.
Backup failed because Unable to write file at location: local-backup-2023-03-06-14-07-05.zip. Not able to write the file.
#0 /var/www/html/vendor/masbug/flysystem-google-drive-ext/src/GoogleDriveAdapter.php(440): League\Flysystem\UnableToWriteFile::atLocation('local-backup-20...', 'Not able to wri...')
#1 /var/www/html/vendor/masbug/flysystem-google-drive-ext/src/GoogleDriveAdapter.php(457): Masbug\Flysystem\GoogleDriveAdapter->writeData('local-backup-20...', Resource id #1138, Object(League\Flysystem\Config))
#2 /var/www/html/vendor/league/flysystem/src/Filesystem.php(66): Masbug\Flysystem\GoogleDriveAdapter->writeStream('local-backup-20...', Resource id #1138, Object(League\Flysystem\Config))
#3 /var/www/html/vendor/spatie/laravel-backup/src/BackupDestination/BackupDestination.php(84): League\Flysystem\Filesystem->writeStream('/local-backup-2...', Resource id #1138, Array)
#4 /var/www/html/vendor/spatie/laravel-backup/src/Tasks/Backup/BackupJob.php(292): Spatie\Backup\BackupDestination\BackupDestination->write('/var/www/html/s...')
#5 /var/www/html/vendor/laravel/framework/src/Illuminate/Collections/Traits/EnumeratesValues.php(235): Spatie\Backup\Tasks\Backup\BackupJob->Spatie\Backup\Tasks\Backup\{closure}(Object(Spatie\Backup\BackupDestination\BackupDestination), 0)
#6 /var/www/html/vendor/spatie/laravel-backup/src/Tasks/Backup/BackupJob.php(284): Illuminate\Support\Collection->each(Object(Closure))
#7 /var/www/html/vendor/spatie/laravel-backup/src/Tasks/Backup/BackupJob.php(168): Spatie\Backup\Tasks\Backup\BackupJob->copyToBackupDestinations('/var/www/html/s...')
#8 /var/www/html/vendor/spatie/laravel-backup/src/Commands/BackupCommand.php(58): Spatie\Backup\Tasks\Backup\BackupJob->run()
#9 /var/www/html/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(36): Spatie\Backup\Commands\BackupCommand->handle()
#10 /var/www/html/vendor/laravel/framework/src/Illuminate/Container/Util.php(41): Illuminate\Container\BoundMethod::Illuminate\Container\{closure}()
#11 /var/www/html/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(93): Illuminate\Container\Util::unwrapIfClosure(Object(Closure))
#12 /var/www/html/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(35): Illuminate\Container\BoundMethod::callBoundMethod(Object(Illuminate\Foundation\Application), Array, Object(Closure))
#13 /var/www/html/vendor/laravel/framework/src/Illuminate/Container/Container.php(661): Illuminate\Container\BoundMethod::call(Object(Illuminate\Foundation\Application), Array, Array, NULL)
#14 /var/www/html/vendor/laravel/framework/src/Illuminate/Console/Command.php(183): Illuminate\Container\Container->call(Array)
#15 /var/www/html/vendor/symfony/console/Command/Command.php(312): Illuminate\Console\Command->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Illuminate\Console\OutputStyle))
#16 /var/www/html/vendor/laravel/framework/src/Illuminate/Console/Command.php(152): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Illuminate\Console\OutputStyle))
#17 /var/www/html/vendor/spatie/laravel-backup/src/Commands/BaseCommand.php(28): Illuminate\Console\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#18 /var/www/html/vendor/symfony/console/Application.php(1022): Spatie\Backup\Commands\BaseCommand->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#19 /var/www/html/vendor/symfony/console/Application.php(314): Symfony\Component\Console\Application->doRunCommand(Object(Spatie\Backup\Commands\BackupCommand), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#20 /var/www/html/vendor/symfony/console/Application.php(168): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#21 /var/www/html/vendor/laravel/framework/src/Illuminate/Console/Application.php(102): Symfony\Component\Console\Application->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#22 /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php(155): Illuminate\Console\Application->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#23 /var/www/html/artisan(35): Illuminate\Foundation\Console\Kernel->handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#24 {main}
Backup failed because: Unable to write file at location: local-backup-2023-03-06-14-07-05.zip. Not able to write the file.

Suspected it might be a permissions issue. But my permissions seem fine. The zip folder is created in the shared drive, but seems to be unfinished.

Unsupported characters

Hi,

Thank you for this one it looks amazing while im trying.

Ive noticed that i cant use character '#' nor ':' in folder/file name. Are there other characters that wont work?

For instance:

$remote_filepath = '[ID #4382] USERNAME: Blah Blah Blag/file.txt'; // this wont work
$remote_filepath = '[ID 4382] USERNAME - Blah Blah Blag/file.txt'; // this will work

Thanks again

File not found after second request

Someone, please tell me what I am doing wrong 🤦

I have a Laravel app that syncs references to files to the database to create a kind of, file explorer. This has been working fine but now I just can't get things to work. So after literally 3/4 hours of debugging and creating a completely fresh Laravel app with only this package installed (over and above the basics) and I've got this test code:

    $files = Storage::disk('google')->files('Renders/Bathroom');
    $file1 = [
        'file' => 'Renders/Bathroom/Bathroom with Black Fixtures.jpg',
        'exists' => Storage::disk('google')->fileExists('Renders/Bathroom/Bathroom with Black Fixtures.jpg')
    ];
    $file2 = [
        'file' => 'Renders/Bathroom/Blue and Grey Bathroom.jpg',
        'exists' => Storage::disk('google')->fileExists('Renders/Bathroom/Blue and Grey Bathroom.jpg')
    ];

    dd([
        'files' => $files,
        'file1' => $file1,
        'file2' => $file2,
    ]);

This is the output of the dd:

^ array:3 [▼
  "files" => array:9 [▼
    0 => "Renders/Bathroom/Bathroom with Black Fixtures.jpg"
    1 => "Renders/Bathroom/Blue and Grey Bathroom.jpg"
    2 => "Renders/Bathroom/Grey and Blue Bathroom.jpg"
    3 => "Renders/Bathroom/Loft Bathroom.jpg"
    4 => "Renders/Bathroom/Marble Bathroom.jpg"
    5 => "Renders/Bathroom/Modern Bathroom.jpg"
    6 => "Renders/Bathroom/Truffle Oak Bathroom.jpg"
    7 => "Renders/Bathroom/White Brick Bathroom.jpg"
    8 => "Renders/Bathroom/White Freestanding Bathroom.jpg"
  ]
  "file1" => array:2 [▼
    "file" => "Renders/Bathroom/Bathroom with Black Fixtures.jpg"
    "exists" => true
  ]
  "file2" => array:2 [▼
    "file" => "Renders/Bathroom/Blue and Grey Bathroom.jpg"
    "exists" => false
  ]
]

It seems that as soon as I call Storage::disk('google')->fileExists('Renders/Bathroom/Bathroom with Black Fixtures.jpg') once, then any subsequent requests to any other files are not found. To test this further, I simply switched the file paths for $file1 and $file2 and I get the same outcome, i.e:


    $files = Storage::disk('google')->files('Renders/Bathroom');
    $file1 = [
        'file' => 'Renders/Bathroom/Blue and Grey Bathroom.jpg',
        'exists' => Storage::disk('google')->fileExists('Renders/Bathroom/Blue and Grey Bathroom.jpg')
    ];
    $file2 = [
        'file' => 'Renders/Bathroom/Bathroom with Black Fixtures.jpg',
        'exists' => Storage::disk('google')->fileExists('Renders/Bathroom/Bathroom with Black Fixtures.jpg')
    ];

    dd([
        'files' => $files,
        'file1' => $file1,
        'file2' => $file2,
    ]);

...

^ array:3 [▼
  "files" => array:9 [▼
    0 => "Renders/Bathroom/Bathroom with Black Fixtures.jpg"
    1 => "Renders/Bathroom/Blue and Grey Bathroom.jpg"
    2 => "Renders/Bathroom/Grey and Blue Bathroom.jpg"
    3 => "Renders/Bathroom/Loft Bathroom.jpg"
    4 => "Renders/Bathroom/Marble Bathroom.jpg"
    5 => "Renders/Bathroom/ Modern Bathroom.jpg"
    6 => "Renders/Bathroom/Truffle Oak Bathroom.jpg"
    7 => "Renders/Bathroom/White Brick Bathroom.jpg"
    8 => "Renders/Bathroom/White Freestanding Bathroom.jpg"
  ]
  "file1" => array:2 [▼
    "file" => "Renders/Bathroom/Blue and Grey Bathroom.jpg"
    "exists" => true
  ]
  "file2" => array:2 [▼
    "file" => "Renders/Bathroom/Bathroom with Black Fixtures.jpg"
    "exists" => false
  ]
]

This is driving me bananas! Any help would be greatly appreciated!

Thanks,
Mat

401 Request had invalid authentication credentials (laravel jobs)

In my Laravel app, I noticed that after uploading files using the queue system for about an hour, I encounter the following error message:

{
  "error": {
    "code": 401,
    "message": "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
    "errors": [
      {
        "message": "Invalid Credentials",
        "domain": "global",
        "reason": "authError",
        "location": "Authorization",
        "locationType": "header"
      }
    ],
    "status": "UNAUTHENTICATED"
  }
}

I found that this issue is resolved by manually restarting the queue using the command php artisan queue:restart

It seems that the refreshToken replaces the refreshed token in memory, but since Laravel jobs run indefinitely, they do not detect changes until the queue is restarted manually. How can I address this issue?

Error: "Properties can not be manipulated"

Hello.

I have a fresh installation of Laravel 9 with php 8.1.2 and I added your package according to your instructions.

When I try to access my Google drive I get this error "Properties can not be manipulated".

Following the breadcrumbs, the problem starts in the file Masbug\Flysystem\GoogleDriveAdapter in the function setHasDir

foreach ($results as $key => $result) {
            if ($result instanceof FileList) {
                $object[$paths[$key]]['hasdir'] = $this->cacheHasDirs[$paths[$key]] = (bool)$result->getFiles();//this is the problem
            }
        }

which uses the function offsetSet in the file League\Flysystem\DirectoryAttributes which uses the trait ProxyArrayAccessToProperties which contains this function.

In this trait the functions offsetSet and offsetUnset throw this exception directly

/**
     * @param mixed $offset
     * @param mixed $value
     */
    #[\ReturnTypeWillChange]
    public function offsetSet($offset, $value): void
    {
        throw new RuntimeException('Properties can not be manipulated');
    }

    /**
     * @param mixed $offset
     */
    #[\ReturnTypeWillChange]
    public function offsetUnset($offset): void
    {
        throw new RuntimeException('Properties can not be manipulated');
    }

If I comment the throw part (I know I am not supposed to), everything seems fine (emphasis on the seems).

Do you have any idea what this issue is about? Has anyone else encountered this?

Thanks.

Upload of images to Google Drive is killing my bandwidth/freezes my site

When executing this line of code:

Storage::disk('google')->put($imageId, base64_decode($src));

my whole app becomes unresponsive. When opening a new browser tab, the page doesn't load before the upload to drive is finished. I assume that the upload to drive burns up my whole upload bandwidth. Is there a throttle option or another way to prevent this from happening?

I'm using drive to store and retrieve images.

Here's my GoogleDriveServiceProvider.php:

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Storage;
use Google\Client;
use Google\Service\Drive;
use Masbug\Flysystem\GoogleDriveAdapter;
use League\Flysystem\Filesystem;
use Illuminate\Filesystem\FilesystemAdapter;


class GoogleDriveServiceProvider extends ServiceProvider
{

    public function register()
    {
        //
    }

    public function boot()
    {
        try {
            Storage::extend('google', function($app, $config) {
                $options = [];

                if (!empty($config['teamDriveId'] ?? null)) {
                    $options['teamDriveId'] = $config['teamDriveId'];
                }

                $client = new Client();
                $client->setClientId($config['clientId']);
                $client->setClientSecret($config['clientSecret']);
                $client->refreshToken($config['refreshToken']);

                $service = new Drive($client);
                
                $adapter = new GoogleDriveAdapter($service, $config['folder'] ?? '/', $options);
                $driver = new Filesystem($adapter);

                return new FilesystemAdapter($driver, $adapter);
            });
        } catch(\Exception $e) {
            return $e->getMessage();
        }
    }
}

Error while creating folder after deleting it from gdrive

Steps to replicate:

  1. Create folder Storage::disk('google')->createDir('test');
  2. Delete 'test' folder in Google drive manually(delete from trash also)
  3. Create same folder Storage::disk('google')->createDir('test');

Locally works, but throws this error on live servers (AWS EC2 Instance Ubuntu)

[2021-07-16 06:59:58] staging.ERROR: Unknown exception encountered while retrieving product or dispatching asset download jobs: {
 "error": {
  "errors": [
   {
    "domain": "global",
    "reason": "notFound",
    "message": "File not found: 1255h34g2AtWNgO4wjJPzzfHlxh_yIIyA.",
    "locationType": "parameter",
    "location": "fileId"
   }
  ],
  "code": 404,
  "message": "File not found: 1255h34g2AtWNgO4wjJPzzfHlxh_yIIyA."
 }
}
 {"asin":"B01LSUQSB0","marketplace":"us","exception":"[object] (Google\\Service\\Exception(code: 404): {
 \"error\": {
  \"errors\": [
   {
    \"domain\": \"global\",
    \"reason\": \"notFound\",
    \"message\": \"File not found: 1255h34g2AtWNgO4wjJPzzfHlxh_yIIyA.\",
    \"locationType\": \"parameter\",
    \"location\": \"fileId\"
   }
  ],
  \"code\": 404,
  \"message\": \"File not found: 1255h34g2AtWNgO4wjJPzzfHlxh_yIIyA.\"
 }
}
 at /var/www/html/contentx/staging/releases/20211507_122037/vendor/google/apiclient/src/Http/REST.php:128)
[stacktrace]
#0 /var/www/html/contentx/staging/releases/20211507_122037/vendor/google/apiclient/src/Http/REST.php(103): Google\\Http\\REST::decodeHttpResponse()
#1 [internal function]: Google\\Http\\REST::doExecute()
#2 /var/www/html/contentx/staging/releases/20211507_122037/vendor/google/apiclient/src/Task/Runner.php(182): call_user_func_array()
#3 /var/www/html/contentx/staging/releases/20211507_122037/vendor/google/apiclient/src/Http/REST.php(66): Google\\Task\\Runner->run()
#4 /var/www/html/contentx/staging/releases/20211507_122037/vendor/google/apiclient/src/Client.php(898): Google\\Http\\REST::execute()
#5 /var/www/html/contentx/staging/releases/20211507_122037/vendor/google/apiclient/src/Service/Resource.php(238): Google\\Client->execute()
#6 /var/www/html/contentx/staging/releases/20211507_122037/vendor/google/apiclient-services/src/Drive/Resource/Files.php(101): Google\\Service\\Resource->call()
#7 /var/www/html/contentx/staging/releases/20211507_122037/vendor/masbug/flysystem-google-drive-ext/src/GoogleDriveAdapter.php(1282): Google\\Service\\Drive\\Resource\\Files->create()
#8 /var/www/html/contentx/staging/releases/20211507_122037/vendor/masbug/flysystem-google-drive-ext/src/GoogleDriveAdapter.php(573): Masbug\\Flysystem\\GoogleDriveAdapter->createDirectory()
#9 /var/www/html/contentx/staging/releases/20211507_122037/vendor/league/flysystem/src/Filesystem.php(263): Masbug\\Flysystem\\GoogleDriveAdapter->createDir()
#10 /var/www/html/contentx/staging/releases/20211507_122037/vendor/laravel/framework/src/Illuminate/Filesystem/FilesystemAdapter.php(787): League\\Flysystem\\Filesystem->createDir()
#11 /var/www/html/contentx/staging/releases/20211507_122037/app/Jobs/GetAsinDataJob.php(95): Illuminate\\Filesystem\\FilesystemAdapter->__call()
#12 /var/www/html/contentx/staging/releases/20211507_122037/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(36): App\\Jobs\\GetAsinDataJob->handle()
#13 /var/www/html/contentx/staging/releases/20211507_122037/vendor/laravel/framework/src/Illuminate/Container/Util.php(40): Illuminate\\Container\\BoundMethod::Illuminate\\Container\\{closure}()
#14 /var/www/html/contentx/staging/releases/20211507_122037/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(93): Illuminate\\Container\\Util::unwrapIfClosure()
#15 /var/www/html/contentx/staging/releases/20211507_122037/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(37): Illuminate\\Container\\BoundMethod::callBoundMethod()
#16 /var/www/html/contentx/staging/releases/20211507_122037/vendor/laravel/framework/src/Illuminate/Container/Container.php(651): Illuminate\\Container\\BoundMethod::call()
#17 /var/www/html/contentx/staging/releases/20211507_122037/vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php(128): Illuminate\\Container\\Container->call()
#18 /var/www/html/contentx/staging/releases/20211507_122037/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(128): Illuminate\\Bus\\Dispatcher->Illuminate\\Bus\\{closure}()
#19 /var/www/html/contentx/staging/releases/20211507_122037/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(103): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#20 /var/www/html/contentx/staging/releases/20211507_122037/vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php(132): Illuminate\\Pipeline\\Pipeline->then()
#21 /var/www/html/contentx/staging/releases/20211507_122037/vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php(120): Illuminate\\Bus\\Dispatcher->dispatchNow()
#22 /var/www/html/contentx/staging/releases/20211507_122037/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(128): Illuminate\\Queue\\CallQueuedHandler->Illuminate\\Queue\\{closure}()
#23 /var/www/html/contentx/staging/releases/20211507_122037/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(103): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#24 /var/www/html/contentx/staging/releases/20211507_122037/vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php(122): Illuminate\\Pipeline\\Pipeline->then()
#25 /var/www/html/contentx/staging/releases/20211507_122037/vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php(70): Illuminate\\Queue\\CallQueuedHandler->dispatchThroughMiddleware()
#26 /var/www/html/contentx/staging/releases/20211507_122037/vendor/laravel/framework/src/Illuminate/Queue/Jobs/Job.php(98): Illuminate\\Queue\\CallQueuedHandler->call()
#27 /var/www/html/contentx/staging/releases/20211507_122037/vendor/laravel/framework/src/Illuminate/Queue/Worker.php(428): Illuminate\\Queue\\Jobs\\Job->fire()
#28 /var/www/html/contentx/staging/releases/20211507_122037/vendor/laravel/framework/src/Illuminate/Queue/Worker.php(378): Illuminate\\Queue\\Worker->process()
#29 /var/www/html/contentx/staging/releases/20211507_122037/vendor/laravel/framework/src/Illuminate/Queue/Worker.php(172): Illuminate\\Queue\\Worker->runJob()
#30 /var/www/html/contentx/staging/releases/20211507_122037/vendor/laravel/framework/src/Illuminate/Queue/Console/WorkCommand.php(117): Illuminate\\Queue\\Worker->daemon()
#31 /var/www/html/contentx/staging/releases/20211507_122037/vendor/laravel/framework/src/Illuminate/Queue/Console/WorkCommand.php(101): Illuminate\\Queue\\Console\\WorkCommand->runWorker()
#32 /var/www/html/contentx/staging/releases/20211507_122037/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(36): Illuminate\\Queue\\Console\\WorkCommand->handle()
#33 /var/www/html/contentx/staging/releases/20211507_122037/vendor/laravel/framework/src/Illuminate/Container/Util.php(40): Illuminate\\Container\\BoundMethod::Illuminate\\Container\\{closure}()
#34 /var/www/html/contentx/staging/releases/20211507_122037/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(93): Illuminate\\Container\\Util::unwrapIfClosure()
#35 /var/www/html/contentx/staging/releases/20211507_122037/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(37): Illuminate\\Container\\BoundMethod::callBoundMethod()
#36 /var/www/html/contentx/staging/releases/20211507_122037/vendor/laravel/framework/src/Illuminate/Container/Container.php(651): Illuminate\\Container\\BoundMethod::call()
#37 /var/www/html/contentx/staging/releases/20211507_122037/vendor/laravel/framework/src/Illuminate/Console/Command.php(136): Illuminate\\Container\\Container->call()
#38 /var/www/html/contentx/staging/releases/20211507_122037/vendor/symfony/console/Command/Command.php(299): Illuminate\\Console\\Command->execute()
#39 /var/www/html/contentx/staging/releases/20211507_122037/vendor/laravel/framework/src/Illuminate/Console/Command.php(121): Symfony\\Component\\Console\\Command\\Command->run()
#40 /var/www/html/contentx/staging/releases/20211507_122037/vendor/symfony/console/Application.php(978): Illuminate\\Console\\Command->run()
#41 /var/www/html/contentx/staging/releases/20211507_122037/vendor/symfony/console/Application.php(295): Symfony\\Component\\Console\\Application->doRunCommand()
#42 /var/www/html/contentx/staging/releases/20211507_122037/vendor/symfony/console/Application.php(167): Symfony\\Component\\Console\\Application->doRun()
#43 /var/www/html/contentx/staging/releases/20211507_122037/vendor/laravel/framework/src/Illuminate/Console/Application.php(92): Symfony\\Component\\Console\\Application->run()
#44 /var/www/html/contentx/staging/releases/20211507_122037/vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php(129): Illuminate\\Console\\Application->run()
#45 /var/www/html/contentx/staging/releases/20211507_122037/artisan(37): Illuminate\\Foundation\\Console\\Kernel->handle()
#46 {main}

Argument #1 ($value) must be of type Countable|array, null given in GoogleDriveAdapter.php:1037

Seems like $file->getPermissions() is returning null when there are no permission.
When PHP is set to strict, that causes error to be thrown Argument #1 ($value) must be of type Countable|array, null given in GoogleDriveAdapter.php:1037.
This is catched in GoogleDriveAdapter:436 and even when file was written, throws error that [League\Flysystem\UnableToWriteFile] Unable to write file at location: 2024/1704150401_test.txt. Not able to write the file.

File download using file's id

masbug/flysystem-google-drive-ext: 2.1
laravel 9

I save the file's id in my table instead of path, So how can download them ? I try the following but it return null instead of stream resource:

$resource = Storage::disk('google')->readStream('1_g53alfVsT-90wwLDwSqD7E_9EZ5wev69Ty'); // this is null

Storage::writeStream('file.pdf', $resource)
League\Flysystem\InvalidStreamProvided : Invalid stream provided, expected stream resource, received NULL

Login Required

I am getting the following error:

{ "error": { "errors": [ { "domain": "global", "reason": "required", "message": "Login Required", "locationType": "header", "location": "Authorization" } ], "code": 401, "message": "Login Required" } }

I have used the clientId, clientSecret and refreshToken in the Google Playground successfully. I have Googled and found others having a similar issue with a similar package, but no real fixes.

I'm using:

"laravel/framework": "^v9.17.0",
"masbug/flysystem-google-drive-ext": "^v2.2.1"

Any help much appreciated.
Roger

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.