masbug/flysystem-google-drive-ext

支持无缝虚拟路径转换的Google Drive Flysystem适配器

资助包维护!
Ko Fi

安装数: 639,601

依赖: 12

建议者: 0

安全: 0

星标: 214

关注者: 7

分支: 60

开放问题: 11

v2.3.0 2023-10-02 06:52 UTC

README

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

Google为每个文件夹和文件使用唯一的ID。这使得与其他使用常规路径的存储服务集成变得困难。

Flysystem适配器通过无缝地将路径从“显示路径”转换为“虚拟路径”,反之亦然,来解决这个问题。

例如:虚拟路径 /Xa3X9GlR6EmbnY1RLVTk5VUtOVkk/0B3X9GlR6EmbnY1RLVTk5VUtOVkk 变为 /My Nice Dir/myFile.ext,并且所有ID处理都是隐藏的。

安装

  • 对于 Flysystem V2/V3Laravel >= 9.x.x
composer require masbug/flysystem-google-drive-ext
  • 对于 Flysystem V1Laravel <= 8.x.x 使用 1.x.x 版本的包
composer require masbug/flysystem-google-drive-ext:"^1.0.0"

获取Google密钥

请按照 Google文档 获取您的 客户端ID、客户端密钥 & 刷新令牌

此外,您还可以通过 @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列表和ID
$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 实现。