Polyscope - The agent-first dev environment for Laravel

Flysystem Google Drive Ext

masbug/flysystem-google-drive-ext image

Flysystem Google Drive Ext stats

Downloads
272.1K
Stars
172
Open Issues
5
Forks
52

View on GitHub →

Flysystem adapter for Google Drive with seamless virtualdisplay path translation

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

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.

Cube

Laravel Newsletter

Join 40k+ other developers and never miss out on new tips, tutorials, and more.


Masbug Flysystem Google Drive Ext Related Articles

Build Custom Admin Panels With Backpack for Laravel image

Build Custom Admin Panels With Backpack for Laravel

Read article
Blastup logo

Blastup

Blastup provides social media enhancement services including buying Instagram likes, followers, and views, with features like instant delivery and a variety of packages to suit different needs.

Blastup
The Certification of Competence for Laravel logo

The Certification of Competence for Laravel

A community-driven, proctored assessment across 4 levels designed to validate real-world Laravel knowledge, from Junior to mastery-level Artisan. Official Vue.js, Official Nuxt, Angular, React, JS certifications also available.

The Certification of Competence for Laravel
LoadForge logo

LoadForge

Scalable load testing for web apps & APIs. Simulate real-world traffic and identify breaking points and performance limits with powerful, scalable load tests designed for Laravel.

LoadForge
Shift logo

Shift

Running an old Laravel version? Instant, automated Laravel upgrades and code modernization to keep your applications fresh.

Shift
Tighten logo

Tighten

We help companies turn great ideas into amazing apps, products, and services.

Tighten
Curotec logo

Curotec

World class Laravel experts with GenAI dev skills. LATAM-based, embedded engineers that ship fast, communicate clearly, and elevate your product. No bloat, no BS.

Curotec