realmatrix / flysystem-google-drive-ext
Google Drive 的 Flysystem 适配器,具有无缝虚拟路径与显示路径转换功能
Requires
- php: ^7.2 | ^8.0
- ext-mbstring: *
- google/apiclient: ^2.2
- guzzlehttp/guzzle: ^6.3 | ^7.0
- guzzlehttp/psr7: ^1.7|^2.0
- league/flysystem: ^2.1.1|^3.0
Requires (Dev)
- league/flysystem-adapter-test-utilities: ^2.0|^3.0
- phpunit/phpunit: ^8.0 | ^9.3
This package is not auto-updated.
Last update: 2024-10-03 05:48:21 UTC
README
Google 使用每个文件夹和文件的唯一 ID。这使得与其他使用正常路径的存储服务集成变得困难。
此 Flysystem 适配器 通过无缝地将路径从“显示路径”转换为“虚拟路径”,反之亦然来解决该问题。
例如:虚拟路径 /Xa3X9GlR6EmbnY1RLVTk5VUtOVkk/0B3X9GlR6EmbnY1RLVTk5VUtOVkk
转换为 /My Nice Dir/myFile.ext
,所有 ID 处理都隐藏起来。
安装
- 对于 Flysystem V2/V3 或 Laravel >= 9.x.x
composer require realmatrix/flysystem-google-drive-ext
- 对于 Flysystem V1 或 Laravel <= 8.x.x 使用包的 1.x.x 版本
composer require realmatrix/flysystem-google-drive-ext:"^1.0.0"
获取 Google 密钥
请按照 Google Docs 获取您的 client ID, client secret & refresh token
。
此外,您还可以通过 @ivanvermeyen 跟随这些易于理解的教程
用法
$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 */);
文件上传
// 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.
文件下载
// 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(); }
如何获取 TeamDrive 列表和 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; }
如何永久删除用户的所有已删除文件
$fs->getAdapter()->emptyTrash([]);
与 Laravel 框架一起使用
更新 .env
文件以包含 Google 密钥
将您创建的密钥添加到您的 .env
文件中,并将 google
设置为您的默认云存储。您可以将 .env.example
文件复制并填写空白。
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
在 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'), ],*/ // ... ],
在 app/Providers/
路径的 ServiceProvider
中添加存储驱动
示例
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 } // ... } // ... }
现在您可以通过这种方式访问驱动器
$googleDisk = Storage::disk('google'); //$secondDisk = Storage::disk('second_google'); //others disks
请注意,只能有一个默认云存储驱动器,由您 .env
(或配置)文件中的 FILESYSTEM_CLOUD
定义。如果您将其设置为 google
,则将成为云驱动器
Storage::cloud(); // refers to Storage::disk('google')
限制
将显示路径用作文件夹和文件的标识符需要它们是唯一的。不幸的是,Google Drive 允许用户创建具有相同(显示)名称的文件和文件夹。在这种情况下,当无法确定唯一路径时,此适配器选择最旧的(第一个)实例。如果较新的重复项是文件夹,并且用户在内部放置一个唯一的文件或文件夹,则适配器将能够正确访问它(因为完整路径是唯一的)。
同一 Google Drive 的并发使用可能会由于文件/文件夹标识符和文件对象的大量缓存而导致意外问题。
致谢
此适配器基于 Naoki Sawada 的优秀 flysystem-google-drive。
它还包含 Google 的 Google_Http_MediaFileUpload 的改编。我添加了对从流中直接进行可恢复上传的支持(避免将数据复制到内存中)。
TeamDrive 的实现由 Maximilian Ruta - Deltachaos 完成。
为 Flysystem V2 重写适配器以及各种修复由 Erik Niebla - erikn69 实现。