masbug/flysystem-google-drive-ext
Flysystem adapter for Google Drive that hides Google’s file/folder IDs by translating between virtual ID paths and human-friendly display paths. Supports Flysystem v2/v3 (Laravel 9+) with seamless path mapping for common filesystem operations.
## Getting Started
### Minimal Setup
1. **Install the Package**
```bash
composer require masbug/flysystem-google-drive-ext
Ensure league/flysystem (v2+) is also installed.
Configure Google Drive Credentials
Store your Google OAuth credentials in .env:
GOOGLE_DRIVE_CLIENT_ID=your_client_id
GOOGLE_DRIVE_CLIENT_SECRET=your_client_secret
GOOGLE_DRIVE_REDIRECT_URI=your_redirect_uri
GOOGLE_DRIVE_TOKEN=your_refresh_token # Optional: Pre-saved token for testing
Define a Filesystem in config/filesystems.php
'disks' => [
'google-drive' => [
'driver' => 'google-drive',
'client_id' => env('GOOGLE_DRIVE_CLIENT_ID'),
'client_secret' => env('GOOGLE_DRIVE_CLIENT_SECRET'),
'redirect_uri' => env('GOOGLE_DRIVE_REDIRECT_URI'),
'token' => env('GOOGLE_DRIVE_TOKEN'),
'root_folder_id' => env('GOOGLE_DRIVE_ROOT_FOLDER_ID', 'root'), // Optional: Custom root
'virtual_root' => env('GOOGLE_DRIVE_VIRTUAL_ROOT', '/'), // Maps to display paths
],
],
First Use Case: Upload a File
use Illuminate\Support\Facades\Storage;
Storage::disk('google-drive')->put('path/in/drive/my-file.txt', 'Hello, Drive!');
path/in/drive to Google Drive’s virtual ID path.Path Translation
/Xa3X9GlR6EmbnY1RLVTk5VUtOVkk) and user-friendly paths (e.g., /My Nice Dir/).// Upload with display path
Storage::disk('google-drive')->put('projects/report.pdf', file_get_contents('local.pdf'));
// Download using virtual path (hidden by adapter)
$contents = Storage::disk('google-drive')->get('Xa3X9GlR6EmbnY1RLVTk5VUtOVkk');
Directory Operations
Storage::disk('google-drive')->mkdir('team/assets');
$files = Storage::disk('google-drive')->files('team/');
// Returns paths like ['team/report.pdf', 'team/image.png'] (not virtual IDs).
Symlinks and Metadata
url(), size(), lastModified()) to work with metadata.Streaming Large Files
Storage::disk()->writeStream() for large uploads:
Storage::disk('google-drive')->writeStream('large-video.mp4', fopen('local.mp4', 'r'));
Laravel Filesystem Integration
config/filesystems.php to replace local/S3 storage for Google Drive.config/filesystems.php:
'disks' => [
'public' => [
'driver' => 'google-drive',
'root' => 'public', // Maps to a virtual root folder
],
],
Customizing Virtual Roots
/) to map to a specific folder:
'virtual_root' => 'company-projects/', // All paths prepended with this.
OAuth Flow
use Masbug\FlysystemGoogleDriveExt\GoogleDriveAdapter;
$adapter = new GoogleDriveAdapter(
$client,
$rootFolderId,
$virtualRoot = '/'
);
Fallback for Missing Files
FileNotFoundException gracefully:
try {
Storage::disk('google-drive')->get('nonexistent.pdf');
} catch (\League\Flysystem\FileNotFoundException $e) {
Log::warning("File not found in Google Drive: {$e->getMessage()}");
}
Testing
$this->actingAs($user)
->fakeGoogleDrive() // Custom trait to mock responses
->assertUploadedToDrive('test.pdf');
Token Expiry
// Example: Refresh token before operations
$adapter->refreshToken($newRefreshToken);
Auth or Cache to store tokens and auto-refresh.Path Case Sensitivity
// Avoid:
Storage::disk('google-drive')->put('File.TXT', $content); // May fail if 'file.txt' exists.
Quota Limits
if (filesize('large-file.zip') > 5 * 1024 * 1024 * 1024) {
throw new \Exception("File too large for Google Drive.");
}
Virtual Path Collisions
/folder and /FOLDER), the adapter may overwrite files.strtolower()).Permission Denied
403 responses.Enable Logging
config/logging.php:
'channels' => [
'google_drive' => [
'driver' => 'single',
'path' => storage_path('logs/google-drive.log'),
'level' => 'debug',
],
],
\Log::debug('Virtual path:', [$virtualPath, $displayPath]);
Inspect Virtual Paths
$adapter = Storage::disk('google-drive')->getAdapter();
$virtualPath = $adapter->applyVirtualPath('my-file.txt');
dd($virtualPath); // Shows the actual Google Drive ID path.
Common Errors
InvalidArgumentException: Invalid root folder ID or malformed paths.Google_Service_Exception: API quota exceeded or permissions issue.
Custom Path Translator
use Masbug\FlysystemGoogleDriveExt\PathTranslator;
class CustomTranslator extends PathTranslator {
protected function translateToVirtual($displayPath) {
// Add custom logic (e.g., tenant IDs)
return parent::translateToVirtual($displayPath);
}
}
$this->app->bind(Masbug\FlysystemGoogleDriveExt\PathTranslator::class, function () {
return new CustomTranslator();
});
Event Listeners
Storage::disk('google-drive')->addListener('postWrite', function ($event) {
Log::info("File uploaded: {$event->path()}");
});
Flysystem Events
$adapter->addListener('file.written', function ($event) {
// Trigger a webhook or update a database.
});
Testing Helpers
trait GoogleDriveTestHelper {
protected function assertFileExistsInDrive($path) {
$this->assertTrue(
Storage::disk
How can I help you explore Laravel packages today?